| OLD | NEW |
| 1 #!/bin/bash | 1 #!/bin/bash |
| 2 | 2 |
| 3 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 # Called by the Keystone system to update the installed application with a new | 7 # Called by the Keystone system to update the installed application with a new |
| 8 # version from a disk image. | 8 # version from a disk image. |
| 9 | 9 |
| 10 # Return values: | 10 # Return values: |
| 11 # 0 Happiness | 11 # 0 Happiness |
| 12 # 1 Unknown failure | 12 # 1 Unknown failure |
| 13 # 2 Basic sanity check destination failure (e.g. ticket points to nothing) | 13 # 2 Basic sanity check source failure (e.g. no app on disk image) |
| 14 # 3 Could not prepare existing installed version to receive update | 14 # 3 Basic sanity check destination failure (e.g. ticket points to nothing) |
| 15 # 4 rsync failed (could not assure presence of Versions directory) | 15 # 4 Update driven by user ticket when a system ticket is also present |
| 16 # 5 rsync failed (could not copy new versioned directory to Versions) | 16 # 5 Could not prepare existing installed version to receive update |
| 17 # 6 rsync failed (could not update outer .app bundle) | 17 # 6 rsync failed (could not assure presence of Versions directory) |
| 18 # 7 Could not get the version, update URL, or channel after update | 18 # 7 rsync failed (could not copy new versioned directory to Versions) |
| 19 # 8 Updated application does not have the version number from the update | 19 # 8 rsync failed (could not update outer .app bundle) |
| 20 # 9 ksadmin failure | 20 # 9 Could not get the version, update URL, or channel after update |
| 21 # 10 Basic sanity check source failure (e.g. no app on disk image) | 21 # 10 Updated application does not have the version number from the update |
| 22 # 11 ksadmin failure |
| 22 | 23 |
| 23 set -e | 24 set -e |
| 24 | 25 |
| 25 # Returns 0 (true) if the parameter exists, is a symbolic link, and appears | 26 # Returns 0 (true) if the parameter exists, is a symbolic link, and appears |
| 26 # writable on the basis of its POSIX permissions. This is used to determine | 27 # writable on the basis of its POSIX permissions. This is used to determine |
| 27 # writeability like test's -w primary, but -w resolves symbolic links and this | 28 # writeability like test's -w primary, but -w resolves symbolic links and this |
| 28 # function does not. | 29 # function does not. |
| 29 function is_writable_symlink() { | 30 function is_writable_symlink() { |
| 30 SYMLINK=${1} | 31 SYMLINK=${1} |
| 31 LINKMODE=$(stat -f %Sp "${SYMLINK}" 2> /dev/null || true) | 32 LINKMODE=$(stat -f %Sp "${SYMLINK}" 2> /dev/null || true) |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 chmod -h 755 "${TEMPLINK}" && \ | 111 chmod -h 755 "${TEMPLINK}" && \ |
| 111 mv -f "${TEMPLINK}" "${SYMLINKDIR}") || true | 112 mv -f "${TEMPLINK}" "${SYMLINKDIR}") || true |
| 112 rm -rf "${TEMPLINKDIR}" | 113 rm -rf "${TEMPLINKDIR}" |
| 113 fi | 114 fi |
| 114 | 115 |
| 115 return 0 | 116 return 0 |
| 116 } | 117 } |
| 117 | 118 |
| 118 # The argument should be the disk image path. Make sure it exists. | 119 # The argument should be the disk image path. Make sure it exists. |
| 119 if [ $# -lt 1 ] || [ ! -d "${1}" ]; then | 120 if [ $# -lt 1 ] || [ ! -d "${1}" ]; then |
| 120 exit 10 | 121 exit 2 |
| 121 fi | 122 fi |
| 122 | 123 |
| 123 # Who we are. | 124 # Who we are. |
| 124 PRODUCT_NAME="Google Chrome" | 125 PRODUCT_NAME="Google Chrome" |
| 125 APP_DIR="${PRODUCT_NAME}.app" | 126 APP_DIR="${PRODUCT_NAME}.app" |
| 126 FRAMEWORK_NAME="${PRODUCT_NAME} Framework" | 127 FRAMEWORK_NAME="${PRODUCT_NAME} Framework" |
| 127 FRAMEWORK_DIR="${FRAMEWORK_NAME}.framework" | 128 FRAMEWORK_DIR="${FRAMEWORK_NAME}.framework" |
| 128 SRC="${1}/${APP_DIR}" | 129 SRC="${1}/${APP_DIR}" |
| 129 | 130 |
| 130 # Make sure that there's something to copy from, and that it's an absolute | 131 # Make sure that there's something to copy from, and that it's an absolute |
| 131 # path. | 132 # path. |
| 132 if [ -z "${SRC}" ] || [ "${SRC:0:1}" != "/" ] || [ ! -d "${SRC}" ] ; then | 133 if [ -z "${SRC}" ] || [ "${SRC:0:1}" != "/" ] || [ ! -d "${SRC}" ] ; then |
| 133 exit 10 | 134 exit 2 |
| 134 fi | 135 fi |
| 135 | 136 |
| 136 # Figure out where we're going. Determine the application version to be | 137 # Figure out where we're going. Determine the application version to be |
| 137 # installed, use that to locate the framework, and then look inside the | 138 # installed, use that to locate the framework, and then look inside the |
| 138 # framework for the Keystone product ID. | 139 # framework for the Keystone product ID. |
| 139 APP_VERSION_KEY="CFBundleShortVersionString" | 140 APP_VERSION_KEY="CFBundleShortVersionString" |
| 140 UPD_VERSION_APP=$(defaults read "${SRC}/Contents/Info" "${APP_VERSION_KEY}" || | 141 UPD_VERSION_APP=$(defaults read "${SRC}/Contents/Info" "${APP_VERSION_KEY}" || |
| 141 exit 10) | 142 exit 2) |
| 142 UPD_KS_PLIST="${SRC}/Contents/Versions/${UPD_VERSION_APP}/${FRAMEWORK_DIR}/Resou
rces/Info" | 143 UPD_KS_PLIST="${SRC}/Contents/Versions/${UPD_VERSION_APP}/${FRAMEWORK_DIR}/Resou
rces/Info" |
| 143 KS_VERSION_KEY="KSVersion" | 144 KS_VERSION_KEY="KSVersion" |
| 144 UPD_VERSION_KS=$(defaults read "${UPD_KS_PLIST}" "${KS_VERSION_KEY}" || exit 10) | 145 UPD_VERSION_KS=$(defaults read "${UPD_KS_PLIST}" "${KS_VERSION_KEY}" || exit 2) |
| 145 PRODUCT_ID=$(defaults read "${UPD_KS_PLIST}" KSProductID || exit 10) | 146 PRODUCT_ID=$(defaults read "${UPD_KS_PLIST}" KSProductID || exit 2) |
| 146 if [ -z "${UPD_VERSION_KS}" ] || [ -z "${PRODUCT_ID}" ] ; then | 147 if [ -z "${UPD_VERSION_KS}" ] || [ -z "${PRODUCT_ID}" ] ; then |
| 147 exit 2 | 148 exit 3 |
| 148 fi | 149 fi |
| 149 DEST=$(ksadmin -pP "${PRODUCT_ID}" | | 150 DEST=$(ksadmin -pP "${PRODUCT_ID}" | |
| 150 sed -Ene \ | 151 sed -Ene \ |
| 151 's%^[[:space:]]+xc=<KSPathExistenceChecker:.* path=(/.+)>$%\1%p') | 152 's%^[[:space:]]+xc=<KSPathExistenceChecker:.* path=(/.+)>$%\1%p') |
| 152 | 153 |
| 153 # More sanity checking. | 154 # More sanity checking. |
| 154 if [ -z "${DEST}" ] || [ ! -d "${DEST}" ]; then | 155 if [ -z "${DEST}" ] || [ ! -d "${DEST}" ]; then |
| 155 exit 2 | 156 exit 3 |
| 157 fi |
| 158 |
| 159 # If this script is not running as root, it's being driven by a user ticket. |
| 160 # If a system ticket is also present, there's a potential for the two to |
| 161 # collide. Both ticket types might be present if another user on the system |
| 162 # promoted the ticket to system: the other user could not have removed this |
| 163 # user's user ticket. Handle that case here by deleting the user ticket and |
| 164 # exiting early with a discrete exit status. |
| 165 # |
| 166 # Current versions of ksadmin will exit 1 (false) when asked to print tickets |
| 167 # and given a specific product ID to print. Older versions of ksadmin would |
| 168 # exit 0 (true), but those same versions did not support -S (meaning to check |
| 169 # the system ticket store) and would exit 1 (false) with this invocation due |
| 170 # to not understanding the question. Therefore, the usage here will only |
| 171 # delete the existing user ticket when running as non-root with access to a |
| 172 # sufficiently recent ksadmin. Older ksadmins are tolerated: the update will |
| 173 # likely fail for another reason and the user ticket will hang around until |
| 174 # something is eventually able to remove it. |
| 175 if [ ${EUID} -ne 0 ] && |
| 176 ksadmin -S --print-tickets -P "${PRODUCT_ID}" >& /dev/null ; then |
| 177 ksadmin --delete -P "${PRODUCT_ID}" || true |
| 178 exit 4 |
| 156 fi | 179 fi |
| 157 | 180 |
| 158 # Figure out what the existing version is using for its versioned directory. | 181 # Figure out what the existing version is using for its versioned directory. |
| 159 # This will be used later, to avoid removing the currently-installed version's | 182 # This will be used later, to avoid removing the currently-installed version's |
| 160 # versioned directory in case anything is still using it. | 183 # versioned directory in case anything is still using it. |
| 161 OLD_VERSION_APP=$(defaults read "${DEST}/Contents/Info" "${APP_VERSION_KEY}" || | 184 OLD_VERSION_APP=$(defaults read "${DEST}/Contents/Info" "${APP_VERSION_KEY}" || |
| 162 true) | 185 true) |
| 163 OLD_VERSIONED_DIR="${DEST}/Contents/Versions/${OLD_VERSION_APP}" | 186 OLD_VERSIONED_DIR="${DEST}/Contents/Versions/${OLD_VERSION_APP}" |
| 164 | 187 |
| 165 # See if the timestamp of what's currently on disk is newer than the update's | 188 # See if the timestamp of what's currently on disk is newer than the update's |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 # directory, otherwise, this directory would be the only one in the entire | 265 # directory, otherwise, this directory would be the only one in the entire |
| 243 # update exempt from getting its permissions copied over. A simple mkdir | 266 # update exempt from getting its permissions copied over. A simple mkdir |
| 244 # wouldn't copy mode bits. This is done even if ${DEST}/Contents/Versions | 267 # wouldn't copy mode bits. This is done even if ${DEST}/Contents/Versions |
| 245 # already does exist to ensure that the mode bits come from the update. | 268 # already does exist to ensure that the mode bits come from the update. |
| 246 # | 269 # |
| 247 # ${DEST} is guaranteed to exist at this point, but ${DEST}/Contents may not | 270 # ${DEST} is guaranteed to exist at this point, but ${DEST}/Contents may not |
| 248 # if things are severely broken or if this update is actually an initial | 271 # if things are severely broken or if this update is actually an initial |
| 249 # installation from a Keystone skeleton bootstrap. The mkdir creates | 272 # installation from a Keystone skeleton bootstrap. The mkdir creates |
| 250 # ${DEST}/Contents if it doesn't exist; its mode bits will be fixed up in a | 273 # ${DEST}/Contents if it doesn't exist; its mode bits will be fixed up in a |
| 251 # subsequent rsync. | 274 # subsequent rsync. |
| 252 mkdir -p "${DEST}/Contents" || exit 3 | 275 mkdir -p "${DEST}/Contents" || exit 5 |
| 253 rsync ${RSYNC_FLAGS} --exclude "*" "${SRC}/Contents/Versions/" \ | 276 rsync ${RSYNC_FLAGS} --exclude "*" "${SRC}/Contents/Versions/" \ |
| 254 "${DEST}/Contents/Versions" || exit 4 | 277 "${DEST}/Contents/Versions" || exit 6 |
| 255 | 278 |
| 256 # Copy the versioned directory. The new versioned directory will have a | 279 # Copy the versioned directory. The new versioned directory will have a |
| 257 # different name than any existing one, so this won't harm anything already | 280 # different name than any existing one, so this won't harm anything already |
| 258 # present in Contents/Versions, including the versioned directory being used | 281 # present in Contents/Versions, including the versioned directory being used |
| 259 # by any running processes. If this step is interrupted, there will be an | 282 # by any running processes. If this step is interrupted, there will be an |
| 260 # incomplete versioned directory left behind, but it won't interfere with | 283 # incomplete versioned directory left behind, but it won't interfere with |
| 261 # anything, and it will be replaced or removed during a future update attempt. | 284 # anything, and it will be replaced or removed during a future update attempt. |
| 262 NEW_VERSIONED_DIR="${DEST}/Contents/Versions/${UPD_VERSION_APP}" | 285 NEW_VERSIONED_DIR="${DEST}/Contents/Versions/${UPD_VERSION_APP}" |
| 263 rsync ${RSYNC_FLAGS} --delete-before \ | 286 rsync ${RSYNC_FLAGS} --delete-before \ |
| 264 "${SRC}/Contents/Versions/${UPD_VERSION_APP}/" \ | 287 "${SRC}/Contents/Versions/${UPD_VERSION_APP}/" \ |
| 265 "${NEW_VERSIONED_DIR}" || exit 5 | 288 "${NEW_VERSIONED_DIR}" || exit 7 |
| 266 | 289 |
| 267 # Copy the unversioned files into place, leaving everything in | 290 # Copy the unversioned files into place, leaving everything in |
| 268 # Contents/Versions alone. If this step is interrupted, the application will | 291 # Contents/Versions alone. If this step is interrupted, the application will |
| 269 # at least remain in a usable state, although it may not pass signature | 292 # at least remain in a usable state, although it may not pass signature |
| 270 # validation. Depending on when this step is interrupted, the application | 293 # validation. Depending on when this step is interrupted, the application |
| 271 # will either launch the old or the new version. The critical point is when | 294 # will either launch the old or the new version. The critical point is when |
| 272 # the main executable is replaced. There isn't very much to copy in this step, | 295 # the main executable is replaced. There isn't very much to copy in this step, |
| 273 # because most of the application is in the versioned directory. This step | 296 # because most of the application is in the versioned directory. This step |
| 274 # only accounts for around 50 files, most of which are small localized | 297 # only accounts for around 50 files, most of which are small localized |
| 275 # InfoPlist.strings files. | 298 # InfoPlist.strings files. |
| 276 rsync ${RSYNC_FLAGS} --delete-after --exclude /Contents/Versions \ | 299 rsync ${RSYNC_FLAGS} --delete-after --exclude /Contents/Versions \ |
| 277 "${SRC}/" "${DEST}" || exit 6 | 300 "${SRC}/" "${DEST}" || exit 8 |
| 278 | 301 |
| 279 # If necessary, touch the outermost .app so that it appears to the outside | 302 # If necessary, touch the outermost .app so that it appears to the outside |
| 280 # world that something was done to the bundle. This will cause LaunchServices | 303 # world that something was done to the bundle. This will cause LaunchServices |
| 281 # to invalidate the information it has cached about the bundle even if | 304 # to invalidate the information it has cached about the bundle even if |
| 282 # lsregister does not run. This is not done if rsync already updated the | 305 # lsregister does not run. This is not done if rsync already updated the |
| 283 # timestamp to something newer than what had been on disk. This is not | 306 # timestamp to something newer than what had been on disk. This is not |
| 284 # considered a critical step, and if it fails, this script will not exit. | 307 # considered a critical step, and if it fails, this script will not exit. |
| 285 if [ -n "${NEEDS_TOUCH}" ] ; then | 308 if [ -n "${NEEDS_TOUCH}" ] ; then |
| 286 touch -cf "${DEST}" || true | 309 touch -cf "${DEST}" || true |
| 287 fi | 310 fi |
| 288 | 311 |
| 289 # Read the new values (e.g. version). Get the installed application version | 312 # Read the new values (e.g. version). Get the installed application version |
| 290 # to get the path to the framework, where the Keystone keys are stored. | 313 # to get the path to the framework, where the Keystone keys are stored. |
| 291 NEW_VERSION_APP=$(defaults read "${DEST}/Contents/Info" "${APP_VERSION_KEY}" || | 314 NEW_VERSION_APP=$(defaults read "${DEST}/Contents/Info" "${APP_VERSION_KEY}" || |
| 292 exit 7) | 315 exit 9) |
| 293 NEW_KS_PLIST="${DEST}/Contents/Versions/${NEW_VERSION_APP}/${FRAMEWORK_DIR}/Reso
urces/Info" | 316 NEW_KS_PLIST="${DEST}/Contents/Versions/${NEW_VERSION_APP}/${FRAMEWORK_DIR}/Reso
urces/Info" |
| 294 NEW_VERSION_KS=$(defaults read "${NEW_KS_PLIST}" "${KS_VERSION_KEY}" || exit 7) | 317 NEW_VERSION_KS=$(defaults read "${NEW_KS_PLIST}" "${KS_VERSION_KEY}" || exit 9) |
| 295 URL=$(defaults read "${NEW_KS_PLIST}" KSUpdateURL || exit 7) | 318 URL=$(defaults read "${NEW_KS_PLIST}" KSUpdateURL || exit 9) |
| 296 # The channel ID is optional. Suppress stderr to prevent Keystone from seeing | 319 # The channel ID is optional. Suppress stderr to prevent Keystone from seeing |
| 297 # possible error output. | 320 # possible error output. |
| 298 CHANNEL_ID=$(defaults read "${NEW_KS_PLIST}" KSChannelID 2>/dev/null || true) | 321 CHANNEL_ID=$(defaults read "${NEW_KS_PLIST}" KSChannelID 2>/dev/null || true) |
| 299 | 322 |
| 300 # Make sure that the update was successful by comparing the version found in | 323 # Make sure that the update was successful by comparing the version found in |
| 301 # the update with the version now on disk. | 324 # the update with the version now on disk. |
| 302 if [ "${NEW_VERSION_KS}" != "${UPD_VERSION_KS}" ]; then | 325 if [ "${NEW_VERSION_KS}" != "${UPD_VERSION_KS}" ]; then |
| 303 exit 8 | 326 exit 10 |
| 304 fi | 327 fi |
| 305 | 328 |
| 306 # Notify LaunchServices. This is not considered a critical step, and | 329 # Notify LaunchServices. This is not considered a critical step, and |
| 307 # lsregister's exit codes shouldn't be confused with this script's own. | 330 # lsregister's exit codes shouldn't be confused with this script's own. |
| 308 /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.fram
ework/Support/lsregister "${DEST}" || true | 331 /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.fram
ework/Support/lsregister "${DEST}" || true |
| 309 | 332 |
| 310 # Notify Keystone. | 333 # Notify Keystone. |
| 311 KSADMIN_VERSION=$(ksadmin --ksadmin-version || true) | 334 KSADMIN_VERSION=$(ksadmin --ksadmin-version || true) |
| 312 if [ -n "${KSADMIN_VERSION}" ] ; then | 335 if [ -n "${KSADMIN_VERSION}" ] ; then |
| 313 # If ksadmin recognizes --ksadmin-version, it will recognize --tag. | 336 # If ksadmin recognizes --ksadmin-version, it will recognize --tag. |
| 314 ksadmin --register \ | 337 ksadmin --register \ |
| 315 -P "${PRODUCT_ID}" \ | 338 -P "${PRODUCT_ID}" \ |
| 316 --version "${NEW_VERSION_KS}" \ | 339 --version "${NEW_VERSION_KS}" \ |
| 317 --xcpath "${DEST}" \ | 340 --xcpath "${DEST}" \ |
| 318 --url "${URL}" \ | 341 --url "${URL}" \ |
| 319 --tag "${CHANNEL_ID}" || exit 9 | 342 --tag "${CHANNEL_ID}" || exit 11 |
| 320 else | 343 else |
| 321 # Older versions of ksadmin don't recognize --tag. The application will | 344 # Older versions of ksadmin don't recognize --tag. The application will |
| 322 # set the tag when it runs. | 345 # set the tag when it runs. |
| 323 ksadmin --register \ | 346 ksadmin --register \ |
| 324 -P "${PRODUCT_ID}" \ | 347 -P "${PRODUCT_ID}" \ |
| 325 --version "${NEW_VERSION_KS}" \ | 348 --version "${NEW_VERSION_KS}" \ |
| 326 --xcpath "${DEST}" \ | 349 --xcpath "${DEST}" \ |
| 327 --url "${URL}" || exit 9 | 350 --url "${URL}" || exit 11 |
| 328 fi | 351 fi |
| 329 | 352 |
| 330 # The remaining steps are not considered critical. | 353 # The remaining steps are not considered critical. |
| 331 set +e | 354 set +e |
| 332 | 355 |
| 333 # Try to clean up old versions that are not in use. The strategy is to keep | 356 # Try to clean up old versions that are not in use. The strategy is to keep |
| 334 # the versioned directory corresponding to the update just applied | 357 # the versioned directory corresponding to the update just applied |
| 335 # (obviously) and the version that was just replaced, and to use ps and lsof | 358 # (obviously) and the version that was just replaced, and to use ps and lsof |
| 336 # to see if it looks like any processes are currently using any other old | 359 # to see if it looks like any processes are currently using any other old |
| 337 # directories. Directories not in use are removed. Old versioned directories | 360 # directories. Directories not in use are removed. Old versioned directories |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 ([ ${OS_MAJOR} -eq 10 ] && [ ${OS_MINOR} -ge 6 ]) ; then | 497 ([ ${OS_MAJOR} -eq 10 ] && [ ${OS_MINOR} -ge 6 ]) ; then |
| 475 # On 10.6, xattr supports -r for recursive operation. | 498 # On 10.6, xattr supports -r for recursive operation. |
| 476 xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null | 499 xattr -d -r "${QUARANTINE_ATTR}" "${DEST}" >& /dev/null |
| 477 else | 500 else |
| 478 # On earlier systems, xattr doesn't support -r, so run xattr via find. | 501 # On earlier systems, xattr doesn't support -r, so run xattr via find. |
| 479 find "${DEST}" -exec xattr -d "${QUARANTINE_ATTR}" {} + >& /dev/null | 502 find "${DEST}" -exec xattr -d "${QUARANTINE_ATTR}" {} + >& /dev/null |
| 480 fi | 503 fi |
| 481 | 504 |
| 482 # Great success! | 505 # Great success! |
| 483 exit 0 | 506 exit 0 |
| OLD | NEW |