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 |