15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/bin/bash -p
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# usage: keystone_install.sh update_dmg_mount_point
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Called by the Keystone system to update the installed application with a new
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# version from a disk image.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Environment variables:
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# GOOGLE_CHROME_UPDATER_DEBUG
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   When set to a non-empty value, additional information about this script's
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   actions will be logged to stderr.  The same debugging information will
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   also be enabled when "Library/Google/Google Chrome Updater Debug" in the
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#   root directory or in ${HOME} exists.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Exit codes:
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  0  Happiness
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  1  Unknown failure
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  2  Basic sanity check source failure (e.g. no app on disk image)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  3  Basic sanity check destination failure (e.g. ticket points to nothing)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  4  Update driven by user ticket when a system ticket is also present
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  5  Could not prepare existing installed version to receive update
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  6  Patch sanity check failure
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  7  rsync failed (could not copy new versioned directory to Versions)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  8  rsync failed (could not update outer .app bundle)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#  9  Could not get the version, update URL, or channel after update
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 10  Updated application does not have the version number from the update
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 11  ksadmin failure
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 12  dirpatcher failed for versioned directory
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 13  dirpatcher failed for outer .app bundle
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The following exit codes are not used by this script, but can be used to
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# convey special meaning to Keystone:
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 66  (unused) success, request reboot
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 77  (unused) try installation again later
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set -eu
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# http://b/2290916: Keystone runs the installation with a restrictive PATH
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# that only includes the directory containing ksadmin, /bin, and /usr/bin.  It
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# does not include /sbin or /usr/sbin.  This script uses lsof, which is in
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# /usr/sbin, and it's conceivable that it might want to use other tools in an
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# sbin directory.  Adjust the path accordingly.
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)export PATH="${PATH}:/sbin:/usr/sbin"
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Environment sanitization.  Clear environment variables that might impact the
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# interpreter's operation.  The |bash -p| invocation on the #! line takes the
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# bite out of BASH_ENV, ENV, and SHELLOPTS (among other features), but
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# clearing them here ensures that they won't impact any shell scripts used as
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# utility programs. SHELLOPTS is read-only and can't be unset, only
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# unexported.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)unset BASH_ENV CDPATH ENV GLOBIGNORE IFS POSIXLY_CORRECT
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)export -n SHELLOPTS
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)set -o pipefail
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)shopt -s nullglob
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ME="$(basename "${0}")"
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)readonly ME
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Workaround for http://code.google.com/p/chromium/issues/detail?id=83180#c3
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# In bash 4.0, "declare VAR" no longer initializes VAR if not already set.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles): ${GOOGLE_CHROME_UPDATER_DEBUG:=}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)err() {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local error="${1}"
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local id=
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${GOOGLE_CHROME_UPDATER_DEBUG}" ]]; then
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    id=": ${$} $(date "+%Y-%m-%d %H:%M:%S %z")"
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${ME}${id}: ${error}" >& 2
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)note() {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local message="${1}"
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${GOOGLE_CHROME_UPDATER_DEBUG}" ]]; then
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "${message}"
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)g_temp_dir=
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)cleanup() {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local status=${?}
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap - EXIT
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap '' HUP INT QUIT TERM
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${status} -ge 128 ]]; then
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "Caught signal $((${status} - 128))"
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${g_temp_dir}" ]]; then
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -rf "${g_temp_dir}"
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit ${status}
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ensure_temp_dir() {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${g_temp_dir}" ]]; then
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Choose a template that won't be a dot directory.  Make it safe by
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # removing leading hyphens, too.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local template="${ME}"
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${template}" =~ ^[-.]+(.*)$ ]]; then
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      template="${BASH_REMATCH[1]}"
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -z "${template}" ]]; then
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      template="keystone_install"
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_temp_dir="$(mktemp -d -t "${template}")"
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "g_temp_dir = ${g_temp_dir}"
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 0 (true) if |symlink| exists, is a symbolic link, and appears
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# writable on the basis of its POSIX permissions.  This is used to determine
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# writability like test's -w primary, but -w resolves symbolic links and this
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# function does not.
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)is_writable_symlink() {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local symlink="${1}"
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local link_mode
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  link_mode="$(stat -f %Sp "${symlink}" 2> /dev/null || true)"
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${link_mode}" ]] || [[ "${link_mode:0:1}" != "l" ]]; then
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local link_user link_group
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  link_user="$(stat -f %u "${symlink}" 2> /dev/null || true)"
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  link_group="$(stat -f %g "${symlink}" 2> /dev/null || true)"
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${link_user}" ]] || [[ -z "${link_group}" ]]; then
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the users match, check the owner-write bit.
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${EUID} -eq "${link_user}" ]]; then
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${link_mode:2:1}" = "w" ]]; then
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the file's group matches any of the groups that this process is a
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # member of, check the group-write bit.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local group_match=
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local group
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for group in "${GROUPS[@]}"; do
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${group}" -eq "${link_group}" ]]; then
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      group_match="y"
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${group_match}" ]]; then
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${link_mode:5:1}" = "w" ]]; then
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check the other-write bit.
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${link_mode:8:1}" = "w" ]]; then
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If |symlink| exists and is a symbolic link, but is not writable according to
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# is_writable_symlink, this function attempts to replace it with a new
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# writable symbolic link.  If |symlink| does not exist, is not a symbolic
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# link, or is already writable, this function does nothing.  This function
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# always returns 0 (true).
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ensure_writable_symlink() {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local symlink="${1}"
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -L "${symlink}" ]] && ! is_writable_symlink "${symlink}"; then
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If ${symlink} refers to a directory, doing this naively might result in
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the new link being placed in that directory, instead of replacing the
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # existing link.  ln -fhs is supposed to handle this case, but it does so
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # by unlinking (removing) the existing symbolic link before creating a new
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # one.  That leaves a small window during which the symbolic link is not
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # present on disk at all.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # To avoid that possibility, a new symbolic link is created in a temporary
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # location and then swapped into place with mv.  An extra temporary
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # directory is used to convince mv to replace the symbolic link: again, if
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # the existing link refers to a directory, "mv newlink oldlink" will
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # actually leave oldlink alone and place newlink into the directory.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # "mv newlink dirname(oldlink)" works as expected, but in order to replace
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # oldlink, newlink must have the same basename, hence the temporary
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # directory.
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local target
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    target="$(readlink "${symlink}" 2> /dev/null || true)"
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -z "${target}" ]]; then
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Error handling strategy: if anything fails, such as the mktemp, ln,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # chmod, or mv, ignore the failure and return 0 (success), leaving the
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # existing state with the non-writable symbolic link intact.  Failures
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # in this function will be difficult to understand and diagnose, and a
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # non-writable symbolic link is not necessarily fatal.  If something else
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # requires a writable symbolic link, allowing it to fail when a symbolic
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # link is not writable is easier to understand than bailing out of the
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # script on failure here.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local symlink_dir temp_link_dir temp_link
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    symlink_dir="$(dirname "${symlink}")"
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    temp_link_dir="$(mktemp -d "${symlink_dir}/.symlink_temp.XXXXXX" || true)"
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -z "${temp_link_dir}" ]]; then
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    temp_link="${temp_link_dir}/$(basename "${symlink}")"
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (ln -fhs "${target}" "${temp_link}" && \
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        chmod -h 755 "${temp_link}" && \
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mv -f "${temp_link}" "${symlink_dir}/") || true
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -rf "${temp_link_dir}"
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# ensure_writable_symlinks_recursive calls ensure_writable_symlink for every
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# symbolic link in |directory|, recursively.
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# In some very weird and rare cases, it is possible to wind up with a user
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# installation that contains symbolic links that the user does not have write
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# permission over.  More on how that might happen later.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If a weird and rare case like this is observed, rsync will exit with an
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# error when attempting to update the times on these symbolic links.  rsync
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# may not be intelligent enough to try creating a new symbolic link in these
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# cases, but this script can be.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The problem occurs when an administrative user first drag-installs the
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# application to /Applications, resulting in the program's user being set to
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the user's own ID.  If, subsequently, a .pkg package is installed over that,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the existing directory ownership will be preserved, but file ownership will
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# be changed to whatever is specified by the package, typically root.  This
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# applies to symbolic links as well.  On a subsequent update, rsync will be
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# able to copy the new files into place, because the user still has permission
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to write to the directories.  If the symbolic link targets are not changing,
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# though, rsync will not replace them, and they will remain owned by root.
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The user will not have permission to update the time on the symbolic links,
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# resulting in an rsync error.
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ensure_writable_symlinks_recursive() {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local directory="${1}"
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This fix-up is not necessary when running as root, because root will
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # always be able to write everything needed.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${EUID} -eq 0 ]]; then
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This step isn't critical.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local set_e=
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${-}" =~ e ]]; then
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_e="y"
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set +e
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Use find -print0 with read -d $'\0' to handle even the weirdest paths.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local symlink
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while IFS= read -r -d $'\0' symlink; do
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensure_writable_symlink "${symlink}"
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done < <(find "${directory}" -type l -print0)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Go back to how things were.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${set_e}" ]]; then
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set -e
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# is_version_ge accepts two version numbers, left and right, and performs a
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# piecewise comparison determining the result of left >= right, returning true
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# (0) if left >= right, and false (1) if left < right. If left or right are
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# missing components relative to the other, the missing components are assumed
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# to be 0, such that 10.6 == 10.6.0.
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)is_version_ge() {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local left="${1}"
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local right="${2}"
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local -a left_array right_array
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IFS=. left_array=(${left})
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IFS=. right_array=(${right})
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local left_count=${#left_array[@]}
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local right_count=${#right_array[@]}
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local count=${left_count}
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${right_count} -lt ${count} ]]; then
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    count=${right_count}
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Compare the components piecewise, as long as there are corresponding
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # components on each side. If left_element and right_element are unequal,
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # a comparison can be made.
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local index=0
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while [[ ${index} -lt ${count} ]]; do
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local left_element="${left_array[${index}]}"
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local right_element="${right_array[${index}]}"
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ ${left_element} -gt ${right_element} ]]; then
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif [[ ${left_element} -lt ${right_element} ]]; then
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 1
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ((++index))
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If there are more components on the left than on the right, continue
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # comparing, assuming 0 for each of the missing components on the right.
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while [[ ${index} -lt ${left_count} ]]; do
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local left_element="${left_array[${index}]}"
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ ${left_element} -gt 0 ]]; then
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 0
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ((++index))
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If there are more components on the right than on the left, continue
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # comparing, assuming 0 for each of the missing components on the left.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while [[ ${index} -lt ${right_count} ]]; do
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local right_element="${right_array[${index}]}"
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ ${right_element} -gt 0 ]]; then
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 1
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ((++index))
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Upon reaching this point, the two version numbers are semantically equal.
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Prints the OS version, as reported by sw_vers -productVersion, to stdout.
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This function operates with "static" variables: it will only check the OS
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# version once per script run.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)g_checked_os_version=
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)g_os_version=
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)os_version() {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${g_checked_os_version}" ]]; then
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_checked_os_version="y"
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_os_version="$(sw_vers -productVersion)"
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "g_os_version = ${g_os_version}"
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${g_os_version}"
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Compares the running OS version against a supplied version number,
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# |check_version|, and returns 0 (true) if the running OS version is greater
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# than or equal to |check_version| according to a piece-wise comparison.
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 1 (false) if the running OS version number cannot be determined or
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# if |check_version| is greater than the running OS version. |check_version|
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# should be a string of the form "major.minor" or "major.minor.micro".
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)is_os_version_ge() {
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local check_version="${1}"
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local os_version="$(os_version)"
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_version_ge "${os_version}" "${check_version}"
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The return value of is_version_ge is used as this function's return value.
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 0 (true) if xattr supports -r for recursive operation.
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)os_xattr_supports_r() {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # xattr -r is supported in Mac OS X 10.6.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_os_version_ge 10.6
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The return value of is_os_version_ge is used as this function's return
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # value.
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Prints the version of ksadmin, as reported by ksadmin --ksadmin-version, to
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# stdout.  This function operates with "static" variables: it will only check
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the ksadmin version once per script run.  If ksadmin is old enough to not
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# support --ksadmin-version, or another error occurs, this function prints an
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# empty string.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)g_checked_ksadmin_version=
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)g_ksadmin_version=
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ksadmin_version() {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${g_checked_ksadmin_version}" ]]; then
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_checked_ksadmin_version="y"
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_ksadmin_version="$(ksadmin --ksadmin-version || true)"
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "g_ksadmin_version = ${g_ksadmin_version}"
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "${g_ksadmin_version}"
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Compares the installed ksadmin version against a supplied version number,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# |check_version|, and returns 0 (true) if the installed Keystone version is
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# greater than or equal to |check_version| according to a piece-wise
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# comparison.  Returns 1 (false) if the installed Keystone version number
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# cannot be determined or if |check_version| is greater than the installed
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Keystone version.  |check_version| should be a string of the form
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "major.minor.micro.build".
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)is_ksadmin_version_ge() {
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local check_version="${1}"
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_version="$(ksadmin_version)"
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_version_ge "${ksadmin_version}" "${check_version}"
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The return value of is_version_ge is used as this function's return value.
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 0 (true) if ksadmin supports --tag.
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ksadmin_supports_tag() {
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_version
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ksadmin_version="$(ksadmin_version)"
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${ksadmin_version}" ]]; then
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # A ksadmin that recognizes --ksadmin-version and provides a version
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # number is new enough to recognize --tag.
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 1
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 0 (true) if ksadmin supports --tag-path and --tag-key.
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ksadmin_supports_tagpath_tagkey() {
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # --tag-path and --tag-key were introduced in Keystone 1.0.7.1306.
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_ksadmin_version_ge 1.0.7.1306
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The return value of is_ksadmin_version_ge is used as this function's
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # return value.
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 0 (true) if ksadmin supports --brand-path and --brand-key.
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ksadmin_supports_brandpath_brandkey() {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # --brand-path and --brand-key were introduced in Keystone 1.0.8.1620.
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_ksadmin_version_ge 1.0.8.1620
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The return value of is_ksadmin_version_ge is used as this function's
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # return value.
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns 0 (true) if ksadmin supports --version-path and --version-key.
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ksadmin_supports_versionpath_versionkey() {
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # --version-path and --version-key were introduced in Keystone 1.0.9.2318.
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  is_ksadmin_version_ge 1.0.9.2318
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The return value of is_ksadmin_version_ge is used as this function's
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # return value.
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Runs "defaults read" to obtain the value of a key in a property list. As
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# with "defaults read", an absolute path to a plist is supplied, without the
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# ".plist" extension.
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# As of Mac OS X 10.8, defaults (and NSUserDefaults and CFPreferences)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# normally communicates with cfprefsd to read and write plists. Changes to a
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# plist file aren't necessarily reflected immediately via this API family when
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# not made through this API family, because cfprefsd may return cached data
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# from a former on-disk version of a plist file instead of reading the current
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# version from disk. The old behavior can be restored by setting the
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# __CFPREFERENCES_AVOID_DAEMON environment variable, although extreme care
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# should be used because portions of the system that use this API family
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# normally and thus use cfprefsd and its cache will become unsynchronized with
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the on-disk state.
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This function is provided to set __CFPREFERENCES_AVOID_DAEMON when calling
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# "defaults read" and thus avoid cfprefsd and its on-disk cache, and is
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# intended only to be used to read values from Info.plist files, which are not
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# preferences. The use of "defaults" for this purpose has always been
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# questionable, but there's no better option to interact with plists from
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# shell scripts. Definitely don't use infoplist_read to read preference
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# plists.
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# This function exists because the update process delivers new copies of
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Info.plist files to the disk behind cfprefsd's back, and if cfprefsd becomes
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# aware of the original version of the file for any reason (such as this
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# script reading values from it via "defaults read"), the new version of the
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file will not be immediately effective or visible via cfprefsd after the
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# update is applied.
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)infoplist_read() {
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  __CFPREFERENCES_AVOID_DAEMON=1 defaults read "${@}"
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)usage() {
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  echo "usage: ${ME} update_dmg_mount_point" >& 2
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main() {
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local update_dmg_mount_point="${1}"
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Early steps are critical.  Don't continue past any failure.
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set -e
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap cleanup EXIT HUP INT QUIT TERM
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly PRODUCT_NAME="Google Chrome"
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_DIR="${PRODUCT_NAME}.app"
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly ALTERNATE_APP_DIR="${PRODUCT_NAME} Canary.app"
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly FRAMEWORK_NAME="${PRODUCT_NAME} Framework"
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly FRAMEWORK_DIR="${FRAMEWORK_NAME}.framework"
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly PATCH_DIR=".patch"
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly CONTENTS_DIR="Contents"
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_PLIST="${CONTENTS_DIR}/Info"
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly VERSIONS_DIR="${CONTENTS_DIR}/Versions"
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly UNROOTED_BRAND_PLIST="Library/Google/Google Chrome Brand"
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly UNROOTED_DEBUG_FILE="Library/Google/Google Chrome Updater Debug"
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_VERSION_KEY="CFBundleShortVersionString"
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly APP_BUNDLEID_KEY="CFBundleIdentifier"
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_VERSION_KEY="KSVersion"
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_PRODUCT_KEY="KSProductID"
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_URL_KEY="KSUpdateURL"
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_CHANNEL_KEY="KSChannelID"
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KS_BRAND_KEY="KSBrandID"
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly QUARANTINE_ATTR="com.apple.quarantine"
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly KEYCHAIN_REAUTHORIZE_DIR=".keychain_reauthorize"
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Don't use rsync -a, because -a expands to -rlptgoD.  -g and -o copy owners
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # and groups, respectively, from the source, and that is undesirable in this
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # case.  -D copies devices and special files; copying devices only works
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # when running as root, so for consistency between privileged and
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # unprivileged operation, this option is omitted as well.
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #  -I, --ignore-times  don't skip files that match in size and mod-time
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #  -l, --links         copy symlinks as symlinks
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #  -r, --recursive     recurse into directories
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #  -p, --perms         preserve permissions
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #  -t, --times         preserve times
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  readonly RSYNC_FLAGS="-Ilprt"
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # It's difficult to get GOOGLE_CHROME_UPDATER_DEBUG set in the environment
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # when this script is called from Keystone.  If a "debug file" exists in
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # either the root directory or the home directory of the user who owns the
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ticket, turn on verbosity.  This may aid debugging.
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -e "/${UNROOTED_DEBUG_FILE}" ]] ||
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ -e ~/"${UNROOTED_DEBUG_FILE}" ]]; then
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    export GOOGLE_CHROME_UPDATER_DEBUG="y"
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "update_dmg_mount_point = ${update_dmg_mount_point}"
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The argument should be the disk image path.  Make sure it exists and that
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # it's an absolute path.
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "checking update"
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${update_dmg_mount_point}" ]] ||
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ "${update_dmg_mount_point:0:1}" != "/" ]] ||
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ! [[ -d "${update_dmg_mount_point}" ]]; then
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "update_dmg_mount_point must be an absolute path to a directory"
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    usage
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 2
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_dir="${update_dmg_mount_point}/${PATCH_DIR}"
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${patch_dir:0:1}" != "/" ]]; then
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "patch_dir = ${patch_dir}"
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "patch_dir must be an absolute path"
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 2
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Figure out if this is an ordinary installation disk image being used as a
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # full update, or a patch.  A patch will have a .patch directory at the root
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # of the disk image containing information about the update, tools to apply
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # it, and the update contents.
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local is_patch=
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local dirpatcher=
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -d "${patch_dir}" ]]; then
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # patch_dir exists and is a directory - this is a patch update.
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_patch="y"
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dirpatcher="${patch_dir}/dirpatcher.sh"
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! [[ -x "${dirpatcher}" ]]; then
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't locate dirpatcher"
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 6
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ -e "${patch_dir}" ]]; then
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # patch_dir exists, but is not a directory - what's that mean?
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "patch_dir = ${patch_dir}"
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "patch_dir must be a directory"
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 2
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # patch_dir does not exist - this is a full "installer."
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    patch_dir=
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "patch_dir = ${patch_dir}"
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "is_patch = ${is_patch}"
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "dirpatcher = ${dirpatcher}"
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The update to install.
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # update_app is the path to the new version of the .app.  It will only be
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # set at this point for a non-patch update.  It is not yet set for a patch
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # update because no such directory exists yet; it will be set later when
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # dirpatcher creates it.
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local update_app=
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # update_version_app_old, patch_app_dir, and patch_versioned_dir will only
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # be set for patch updates.
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local update_version_app_old=
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_app_dir=
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local patch_versioned_dir=
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local update_version_app update_version_ks product_id
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${is_patch}" ]]; then
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    update_app="${update_dmg_mount_point}/${APP_DIR}"
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_app = ${update_app}"
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Make sure that it's an absolute path.
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${update_app:0:1}" != "/" ]]; then
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "update_app must be an absolute path"
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Make sure there's something to copy from.
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! [[ -d "${update_app}" ]]; then
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      update_app="${update_dmg_mount_point}/${ALTERNATE_APP_DIR}"
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "update_app = ${update_app}"
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if [[ "${update_app:0:1}" != "/" ]]; then
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        err "update_app (alternate) must be an absolute path"
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        exit 2
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fi
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ! [[ -d "${update_app}" ]]; then
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        err "update_app must be a directory"
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        exit 2
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fi
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get some information about the update.
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "reading update values"
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local update_app_plist="${update_app}/${APP_PLIST}"
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_app_plist = ${update_app_plist}"
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! update_version_app="$(infoplist_read "${update_app_plist}" \
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              "${APP_VERSION_KEY}")" ||
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${update_version_app}" ]]; then
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine update_version_app"
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_version_app = ${update_version_app}"
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local update_ks_plist="${update_app_plist}"
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_ks_plist = ${update_ks_plist}"
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! update_version_ks="$(infoplist_read "${update_ks_plist}" \
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             "${KS_VERSION_KEY}")" ||
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${update_version_ks}" ]]; then
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine update_version_ks"
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_version_ks = ${update_version_ks}"
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! product_id="$(infoplist_read "${update_ks_plist}" \
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      "${KS_PRODUCT_KEY}")" ||
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${product_id}" ]]; then
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine product_id"
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "product_id = ${product_id}"
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else  # [[ -n "${is_patch}" ]]
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Get some information about the update.
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "reading update values"
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! update_version_app_old=$(<"${patch_dir}/old_app_version") ||
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${update_version_app_old}" ]]; then
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine update_version_app_old"
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_version_app_old = ${update_version_app_old}"
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! update_version_app=$(<"${patch_dir}/new_app_version") ||
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${update_version_app}" ]]; then
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine update_version_app"
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_version_app = ${update_version_app}"
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! update_version_ks=$(<"${patch_dir}/new_ks_version") ||
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${update_version_ks}" ]]; then
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine update_version_ks"
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_version_ks = ${update_version_ks}"
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! product_id=$(<"${patch_dir}/ks_product") ||
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ -z "${product_id}" ]]; then
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't determine product_id"
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 2
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "product_id = ${product_id}"
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    patch_app_dir="${patch_dir}/application.dirpatch"
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! [[ -d "${patch_app_dir}" ]]; then
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't locate patch_app_dir"
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 6
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "patch_app_dir = ${patch_app_dir}"
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    patch_versioned_dir=\
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"${patch_dir}/version_${update_version_app_old}_${update_version_app}.dirpatch"
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! [[ -d "${patch_versioned_dir}" ]]; then
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "couldn't locate patch_versioned_dir"
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 6
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "patch_versioned_dir = ${patch_versioned_dir}"
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ksadmin is required. Keystone should have set a ${PATH} that includes it.
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Check that here, so that more useful feedback can be offered in the
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # unlikely event that ksadmin is missing.
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "checking Keystone"
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_path
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! ksadmin_path="$(type -p ksadmin)" || [[ -z "${ksadmin_path}" ]]; then
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't locate ksadmin_path"
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 3
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "ksadmin_path = ${ksadmin_path}"
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Call ksadmin_version once to prime the global state.  This is needed
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # because subsequent calls to ksadmin_version that occur in $(...)
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # expansions will not affect the global state (although they can read from
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the already-initialized global state) and thus will cause a new ksadmin
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # --ksadmin-version process to run for each check unless the globals have
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # been properly initialized beforehand.
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ksadmin_version >& /dev/null || true
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_version_string
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ksadmin_version_string="$(ksadmin_version 2> /dev/null || true)"
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "ksadmin_version_string = ${ksadmin_version_string}"
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Figure out where to install.
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local installed_app
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! installed_app="$(ksadmin -pP "${product_id}" | sed -Ene \
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "s%^[[:space:]]+xc=<KSPathExistenceChecker:.* path=(/.+)>\$%\\1%p")" ||
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      [[ -z "${installed_app}" ]]; then
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't locate installed_app"
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 3
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "installed_app = ${installed_app}"
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${installed_app:0:1}" != "/" ]] ||
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ! [[ -d "${installed_app}" ]]; then
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "installed_app must be an absolute path to a directory"
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 3
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If this script is running as root, it's being driven by a system ticket.
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Otherwise, it's being driven by a user ticket.
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local system_ticket=
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ ${EUID} -eq 0 ]]; then
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    system_ticket="y"
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "system_ticket = ${system_ticket}"
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If this script is being driven by a user ticket, but a system ticket is
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # also present, there's a potential for the two to collide.  Both ticket
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # types might be present if another user on the system promoted the ticket
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to system: the other user could not have removed this user's user ticket.
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Handle that case here by deleting the user ticket and exiting early with
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # a discrete exit code.
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Current versions of ksadmin will exit 1 (false) when asked to print tickets
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # and given a specific product ID to print.  Older versions of ksadmin would
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # exit 0 (true), but those same versions did not support -S (meaning to check
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the system ticket store) and would exit 1 (false) with this invocation due
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to not understanding the question.  Therefore, the usage here will only
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # delete the existing user ticket when running as non-root with access to a
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # sufficiently recent ksadmin.  Older ksadmins are tolerated: the update will
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # likely fail for another reason and the user ticket will hang around until
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # something is eventually able to remove it.
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${system_ticket}" ]] &&
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ksadmin -S --print-tickets --productid "${product_id}" >& /dev/null; then
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin --delete --productid "${product_id}" || true
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "can't update on a user ticket when a system ticket is also present"
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 4
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Figure out what the existing installed application is using for its
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # versioned directory.  This will be used later, to avoid removing the
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # existing installed version's versioned directory in case anything is still
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # using it.
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "reading install values"
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local installed_app_plist="${installed_app}/${APP_PLIST}"
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "installed_app_plist = ${installed_app_plist}"
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local installed_app_plist_path="${installed_app_plist}.plist"
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "installed_app_plist_path = ${installed_app_plist_path}"
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_version_app
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  old_version_app="$(infoplist_read "${installed_app_plist}" \
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    "${APP_VERSION_KEY}" || true)"
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "old_version_app = ${old_version_app}"
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # old_version_app is not required, because it won't be present in skeleton
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # bootstrap installations, which just have an empty .app directory.  Only
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # require it when doing a patch update, and use it to validate that the
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # patch applies to the old installed version.  By definition, skeleton
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # bootstraps can't be installed with patch updates.  They require the full
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # application on the disk image.
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${is_patch}" ]]; then
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -z "${old_version_app}" ]]; then
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "old_version_app required for patch"
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 6
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif [[ "${old_version_app}" != "${update_version_app_old}" ]]; then
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "this patch does not apply to the installed version"
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 6
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local installed_versions_dir="${installed_app}/${VERSIONS_DIR}"
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "installed_versions_dir = ${installed_versions_dir}"
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the installed application is incredibly old, old_versioned_dir may not
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # exist.
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_versioned_dir
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${old_version_app}" ]]; then
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    old_versioned_dir="${installed_versions_dir}/${old_version_app}"
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "old_versioned_dir = ${old_versioned_dir}"
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Collect the installed application's brand code, it will be used later.  It
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # is not an error for the installed application to not have a brand code.
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_ks_plist="${installed_app_plist}"
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "old_ks_plist = ${old_ks_plist}"
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local old_brand
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  old_brand="$(infoplist_read "${old_ks_plist}" \
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              "${KS_BRAND_KEY}" 2> /dev/null ||
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               true)"
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "old_brand = ${old_brand}"
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ensure_writable_symlinks_recursive "${installed_app}"
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # By copying to ${installed_app}, the existing application name will be
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # preserved, if the user has renamed the application on disk.  Respecting
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the user's changes is friendly.
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Make sure that ${installed_versions_dir} exists, so that it can receive
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the versioned directory.  It may not exist if updating from an older
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # version that did not use the versioned layout on disk.  Later, during the
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # rsync to copy the application directory, the mode bits and timestamp on
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ${installed_versions_dir} will be set to conform to whatever is present in
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the update.
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ${installed_app} is guaranteed to exist at this point, but
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ${installed_app}/${CONTENTS_DIR} may not if things are severely broken or
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # if this update is actually an initial installation from a Keystone
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # skeleton bootstrap.  The mkdir creates ${installed_app}/${CONTENTS_DIR} if
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # it doesn't exist; its mode bits will be fixed up in a subsequent rsync.
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "creating installed_versions_dir"
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! mkdir -p "${installed_versions_dir}"; then
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "mkdir of installed_versions_dir failed"
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 5
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_versioned_dir
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  new_versioned_dir="${installed_versions_dir}/${update_version_app}"
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "new_versioned_dir = ${new_versioned_dir}"
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If there's an entry at ${new_versioned_dir} but it's not a directory
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # (or it's a symbolic link, whether or not it points to a directory), rsync
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # won't get rid of it.  It's never correct to have a non-directory in place
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # of the versioned directory, so toss out whatever's there.  Don't treat
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # this as a critical step: if removal fails, operation can still proceed to
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to the dirpatcher or rsync, which will likely fail.
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -e "${new_versioned_dir}" ]] &&
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     ([[ -L "${new_versioned_dir}" ]] ||
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ! [[ -d "${new_versioned_dir}" ]]); then
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "removing non-directory in place of versioned directory"
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -f "${new_versioned_dir}" 2> /dev/null || true
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local update_versioned_dir
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${is_patch}" ]]; then
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    update_versioned_dir="${update_app}/${VERSIONS_DIR}/${update_version_app}"
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_versioned_dir = ${update_versioned_dir}"
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else  # [[ -n "${is_patch}" ]]
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # dirpatcher won't patch into a directory that already exists.  Doing so
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # would be a bad idea, anyway.  If ${new_versioned_dir} already exists,
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # it may be something left over from a previous failed or incomplete
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # update attempt, or it may be the live versioned directory if this is a
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # same-version update intended only to change channels.  Since there's no
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # way to tell, this case is handled by having dirpatcher produce the new
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # versioned directory in a temporary location and then having rsync copy
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # it into place as an ${update_versioned_dir}, the same as in a non-patch
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # update.  If ${new_versioned_dir} doesn't exist, dirpatcher can place the
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # new versioned directory at that location directly.
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local versioned_dir_target
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! [[ -e "${new_versioned_dir}" ]]; then
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      versioned_dir_target="${new_versioned_dir}"
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "versioned_dir_target = ${versioned_dir_target}"
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ensure_temp_dir
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      versioned_dir_target="${g_temp_dir}/${update_version_app}"
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "versioned_dir_target = ${versioned_dir_target}"
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      update_versioned_dir="${versioned_dir_target}"
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "update_versioned_dir = ${update_versioned_dir}"
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "dirpatching versioned directory"
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! "${dirpatcher}" "${old_versioned_dir}" \
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         "${patch_versioned_dir}" \
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         "${versioned_dir_target}"; then
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "dirpatcher of versioned directory failed, status ${PIPESTATUS[0]}"
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 12
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Copy the versioned directory.  The new versioned directory should have a
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # different name than any existing one, so this won't harm anything already
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # present in ${installed_versions_dir}, including the versioned directory
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # being used by any running processes.  If this step is interrupted, there
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # will be an incomplete versioned directory left behind, but it won't
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # won't interfere with anything, and it will be replaced or removed during a
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # future update attempt.
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # In certain cases, same-version updates are distributed to move users
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # between channels; when this happens, the contents of the versioned
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # directories are identical and rsync will not render the versioned
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # directory unusable even for an instant.
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ${update_versioned_dir} may be empty during a patch update (${is_patch})
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # if the dirpatcher above was able to write it into place directly.  In
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # that event, dirpatcher guarantees that ${new_versioned_dir} is already in
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # place.
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${update_versioned_dir}" ]]; then
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "rsyncing versioned directory"
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! rsync ${RSYNC_FLAGS} --delete-before "${update_versioned_dir}/" \
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              "${new_versioned_dir}"; then
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "rsync of versioned directory failed, status ${PIPESTATUS[0]}"
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 7
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${is_patch}" ]]; then
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If the versioned directory was prepared in a temporary directory and
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # then rsynced into place, remove the temporary copy now that it's no
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # longer needed.
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -n "${update_versioned_dir}" ]]; then
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rm -rf "${update_versioned_dir}" 2> /dev/null || true
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      update_versioned_dir=
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "update_versioned_dir = ${update_versioned_dir}"
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Prepare ${update_app}.  This always needs to be done in a temporary
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # location because dirpatcher won't write to a directory that already
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # exists, and ${installed_app} needs to be used as input to dirpatcher
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # in any event.  The new application will be rsynced into place once
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # dirpatcher creates it.
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ensure_temp_dir
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    update_app="${g_temp_dir}/${APP_DIR}"
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_app = ${update_app}"
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "dirpatching app directory"
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ! "${dirpatcher}" "${installed_app}" \
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         "${patch_app_dir}" \
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         "${update_app}"; then
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      err "dirpatcher of app directory failed, status ${PIPESTATUS[0]}"
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      exit 13
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # See if the timestamp of what's currently on disk is newer than the
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # update's outer .app's timestamp.  rsync will copy the update's timestamp
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # over, but if that timestamp isn't as recent as what's already on disk, the
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # .app will need to be touched.
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local needs_touch=
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${installed_app}" -nt "${update_app}" ]]; then
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needs_touch="y"
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "needs_touch = ${needs_touch}"
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Copy the unversioned files into place, leaving everything in
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ${installed_versions_dir} alone.  If this step is interrupted, the
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # application will at least remain in a usable state, although it may not
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # pass signature validation.  Depending on when this step is interrupted,
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the application will either launch the old or the new version.  The
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # critical point is when the main executable is replaced.  There isn't very
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # much to copy in this step, because most of the application is in the
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # versioned directory.  This step only accounts for around 50 files, most of
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # which are small localized InfoPlist.strings files.  Note that
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # ${VERSIONS_DIR} is included to copy its mode bits and timestamp, but its
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # contents are excluded, having already been installed above.
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "rsyncing app directory"
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! rsync ${RSYNC_FLAGS} --delete-after --exclude "/${VERSIONS_DIR}/*" \
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       "${update_app}/" "${installed_app}"; then
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "rsync of app directory failed, status ${PIPESTATUS[0]}"
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 8
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "rsyncs complete"
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${is_patch}" ]]; then
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # update_app has been rsynced into place and is no longer needed.
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -rf "${update_app}" 2> /dev/null || true
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    update_app=
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "update_app = ${update_app}"
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${g_temp_dir}" ]]; then
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The temporary directory, if any, is no longer needed.
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -rf "${g_temp_dir}" 2> /dev/null || true
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    g_temp_dir=
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "g_temp_dir = ${g_temp_dir}"
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If necessary, touch the outermost .app so that it appears to the outside
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # world that something was done to the bundle.  This will cause
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # LaunchServices to invalidate the information it has cached about the
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # bundle even if lsregister does not run.  This is not done if rsync already
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # updated the timestamp to something newer than what had been on disk.  This
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # is not considered a critical step, and if it fails, this script will not
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # exit.
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${needs_touch}" ]]; then
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch -cf "${installed_app}" || true
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Read the new values, such as the version.
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "reading new values"
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_version_app
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! new_version_app="$(infoplist_read "${installed_app_plist}" \
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         "${APP_VERSION_KEY}")" ||
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ -z "${new_version_app}" ]]; then
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't determine new_version_app"
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 9
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "new_version_app = ${new_version_app}"
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_versioned_dir="${installed_versions_dir}/${new_version_app}"
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "new_versioned_dir = ${new_versioned_dir}"
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_ks_plist="${installed_app_plist}"
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "new_ks_plist = ${new_ks_plist}"
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local new_version_ks
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! new_version_ks="$(infoplist_read "${new_ks_plist}" \
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        "${KS_VERSION_KEY}")" ||
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ -z "${new_version_ks}" ]]; then
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't determine new_version_ks"
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 9
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "new_version_ks = ${new_version_ks}"
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local update_url
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! update_url="$(infoplist_read "${new_ks_plist}" "${KS_URL_KEY}")" ||
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     [[ -z "${update_url}" ]]; then
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "couldn't determine update_url"
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 9
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "update_url = ${update_url}"
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The channel ID is optional.  Suppress stderr to prevent Keystone from
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # seeing possible error output.
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local channel
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  channel="$(infoplist_read "${new_ks_plist}" \
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            "${KS_CHANNEL_KEY}" 2> /dev/null || true)"
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "channel = ${channel}"
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Make sure that the update was successful by comparing the version found in
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the update with the version now on disk.
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${new_version_ks}" != "${update_version_ks}" ]]; then
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "new_version_ks and update_version_ks do not match"
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 10
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Notify LaunchServices.  This is not considered a critical step, and
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # lsregister's exit codes shouldn't be confused with this script's own.
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Redirect stdout to /dev/null to suppress the useless "ThrottleProcessIO:
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # throttling disk i/o" messages that lsregister might print.
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "notifying LaunchServices"
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local coreservices="/System/Library/Frameworks/CoreServices.framework"
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local launchservices="${coreservices}/Frameworks/LaunchServices.framework"
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local lsregister="${launchservices}/Support/lsregister"
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "coreservices = ${coreservices}"
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "launchservices = ${launchservices}"
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "lsregister = ${lsregister}"
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  "${lsregister}" -f "${installed_app}" > /dev/null || true
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The brand information is stored differently depending on whether this is
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # running for a system or user ticket.
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "handling brand code"
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local set_brand_file_access=
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local brand_plist
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -n "${system_ticket}" ]]; then
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # System ticket.
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_brand_file_access="y"
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    brand_plist="/${UNROOTED_BRAND_PLIST}"
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # User ticket.
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    brand_plist=~/"${UNROOTED_BRAND_PLIST}"
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local brand_plist_path="${brand_plist}.plist"
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "set_brand_file_access = ${set_brand_file_access}"
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "brand_plist = ${brand_plist}"
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "brand_plist_path = ${brand_plist_path}"
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_brand_plist_path
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_brand_key
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Only the stable channel, identified by an empty channel string, has a
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # brand code. On the beta and dev channels, remove the brand plist if
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # present. Its presence means that the ticket used to manage a
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # stable-channel Chrome but the user has since replaced it with a beta or
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # dev channel version. Since the canary channel can run side-by-side with
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # another Chrome installation, don't remove the brand plist on that channel,
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # but skip the rest of the brand logic.
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ "${channel}" = "beta" ]] || [[ "${channel}" = "dev" ]]; then
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "defeating brand code on channel ${channel}"
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rm -f "${brand_plist_path}" 2>/dev/null || true
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  elif [[ -n "${channel}" ]]; then
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Canary channel.
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "skipping brand code on channel ${channel}"
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Stable channel.
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If the user manually updated their copy of Chrome, there might be new
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # brand information in the app bundle, and that needs to be copied out
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # into the file Keystone looks at.
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -n "${old_brand}" ]]; then
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local brand_dir
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      brand_dir="$(dirname "${brand_plist_path}")"
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "brand_dir = ${brand_dir}"
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ! mkdir -p "${brand_dir}"; then
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        err "couldn't mkdir brand_dir, continuing"
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ! defaults write "${brand_plist}" "${KS_BRAND_KEY}" \
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            -string "${old_brand}"; then
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          err "couldn't write brand_plist, continuing"
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elif [[ -n "${set_brand_file_access}" ]]; then
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if ! chown "root:wheel" "${brand_plist_path}"; then
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            err "couldn't chown brand_plist_path, continuing"
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          else
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if ! chmod 644 "${brand_plist_path}"; then
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              err "couldn't chmod brand_plist_path, continuing"
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            fi
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          fi
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        fi
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fi
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Confirm that the brand file exists.  It's optional.
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin_brand_plist_path="${brand_plist_path}"
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin_brand_key="${KS_BRAND_KEY}"
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ ! -f "${ksadmin_brand_plist_path}" ]]; then
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Clear any branding information.
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ksadmin_brand_plist_path=
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ksadmin_brand_key=
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "ksadmin_brand_plist_path = ${ksadmin_brand_plist_path}"
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "ksadmin_brand_key = ${ksadmin_brand_key}"
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "notifying Keystone"
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local ksadmin_args=(
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --register
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --productid "${product_id}"
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --version "${new_version_ks}"
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --xcpath "${installed_app}"
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    --url "${update_url}"
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  )
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ksadmin_supports_tag; then
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin_args+=(
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --tag "${channel}"
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ksadmin_supports_tagpath_tagkey; then
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin_args+=(
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --tag-path "${installed_app_plist_path}"
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --tag-key "${KS_CHANNEL_KEY}"
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ksadmin_supports_brandpath_brandkey; then
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin_args+=(
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --brand-path "${ksadmin_brand_plist_path}"
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --brand-key "${ksadmin_brand_key}"
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ksadmin_supports_versionpath_versionkey; then
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ksadmin_args+=(
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --version-path "${installed_app_plist_path}"
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --version-key "${KS_VERSION_KEY}"
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    )
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "ksadmin_args = ${ksadmin_args[*]}"
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ! ksadmin "${ksadmin_args[@]}"; then
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    err "ksadmin failed"
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    exit 11
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The remaining steps are not considered critical.
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set +e
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Try to clean up old versions that are not in use.  The strategy is to keep
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the versioned directory corresponding to the update just applied
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # (obviously) and the version that was just replaced, and to use ps and lsof
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to see if it looks like any processes are currently using any other old
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # directories.  Directories not in use are removed.  Old versioned
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # directories that are in use are left alone so as to not interfere with
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # running processes.  These directories can be cleaned up by this script on
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # future updates.
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # To determine which directories are in use, both ps and lsof are used.
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Each approach has limitations.
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The ps check looks for processes within the versioned directory.  Only
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # helper processes, such as renderers, are within the versioned directory.
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Browser processes are not, so the ps check will not find them, and will
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # assume that a versioned directory is not in use if a browser is open
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # without any windows.  The ps mechanism can also only detect processes
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # running on the system that is performing the update.  If network shares
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # are involved, all bets are off.
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # The lsof check looks to see what processes have the framework dylib open.
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Browser processes will have their versioned framework dylib open, so this
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # check is able to catch browsers even if there are no associated helper
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # processes.  Like the ps check, the lsof check is limited to processes on
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the system that is performing the update.  Finally, unless running as
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # root, the lsof check can only find processes running as the effective user
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # performing the update.
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # These limitations are motivations to additionally preserve the versioned
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # directory corresponding to the version that was just replaced.
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "cleaning up old versioned directories"
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local versioned_dir
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for versioned_dir in "${installed_versions_dir}/"*; do
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "versioned_dir = ${versioned_dir}"
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${versioned_dir}" = "${new_versioned_dir}" ]] || \
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       [[ "${versioned_dir}" = "${old_versioned_dir}" ]]; then
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # This is the versioned directory corresponding to the update that was
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # just applied or the version that was previously in use.  Leave it
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # alone.
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "versioned_dir is new_versioned_dir or old_versioned_dir, skipping"
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Look for any processes whose executables are within this versioned
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # directory.  They'll be helper processes, such as renderers.  Their
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # existence indicates that this versioned directory is currently in use.
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local ps_string="${versioned_dir}/"
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "ps_string = ${ps_string}"
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Look for any processes using the framework dylib.  This will catch
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # browser processes where the ps check will not, but it is limited to
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # processes running as the effective user.
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local lsof_file="${versioned_dir}/${FRAMEWORK_DIR}/${FRAMEWORK_NAME}"
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "lsof_file = ${lsof_file}"
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # ps -e displays all users' processes, -ww causes ps to not truncate
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # lines, -o comm instructs it to only print the command name, and the =
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # tells it to not print a header line.
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # The cut invocation filters the ps output to only have at most the number
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # of characters in ${ps_string}.  This is done so that grep can look for
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # an exact match.
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # grep -F tells grep to look for lines that are exact matches (not regular
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # expressions), -q tells it to not print any output and just indicate
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # matches by exit status, and -x tells it that the entire line must match
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # ${ps_string} exactly, as opposed to matching a substring.  A match
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # causes grep to exit zero (true).
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    #
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # lsof will exit nonzero if ${lsof_file} does not exist or is open by any
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # process.  If the file exists and is open, it will exit zero (true).
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (! ps -ewwo comm= | \
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          cut -c "1-${#ps_string}" | \
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          grep -Fqx "${ps_string}") &&
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       (! lsof "${lsof_file}" >& /dev/null); then
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # It doesn't look like anything is using this versioned directory.  Get
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # rid of it.
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "versioned_dir doesn't appear to be in use, removing"
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rm -rf "${versioned_dir}"
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "versioned_dir is in use, skipping"
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  done
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If this script is being driven by a user Keystone ticket, it is not
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # running as root.  If the application is installed somewhere under
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # /Applications, try to make it writable by all admin users.  This will
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # allow other admin users to update the application from their own user
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Keystone instances.
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If the script is being driven by a user Keystone ticket (not running as
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # root) and the application is not installed under /Applications, it might
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # not be in a system-wide location, and it probably won't be something that
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # other users on the system are running, so err on the side of safety and
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # don't make it group-writable.
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If this script is being driven by a system ticket (running as root), it's
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # future updates can be expected to be applied the same way, so admin-
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # writability is not a concern.  Set the entire thing to be owned by root
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # in that case, regardless of where it's installed, and drop any group and
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # other write permission.
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If this script is running as a user that is not a member of the admin
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # group, the chgrp operation will not succeed.  Tolerate that case, because
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # it's better than the alternative, which is to make the application
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # world-writable.
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "setting permissions"
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  local chmod_mode="a+rX,u+w,go-w"
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${system_ticket}" ]]; then
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ "${installed_app:0:14}" = "/Applications/" ]] &&
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       chgrp -Rh admin "${installed_app}" 2> /dev/null; then
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      chmod_mode="a+rX,ug+w,o-w"
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chown -Rh root:wheel "${installed_app}" 2> /dev/null
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "chmod_mode = ${chmod_mode}"
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  chmod -R "${chmod_mode}" "${installed_app}" 2> /dev/null
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # On the Mac, or at least on HFS+, symbolic link permissions are significant,
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # but chmod -R and -h can't be used together.  Do another pass to fix the
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # permissions on any symbolic links.
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  find "${installed_app}" -type l -exec chmod -h "${chmod_mode}" {} + \
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      2> /dev/null
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If an update is triggered from within the application itself, the update
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # process inherits the quarantine bit (LSFileQuarantineEnabled).  Any files
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # or directories created during the update will be quarantined in that case,
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # which may cause Launch Services to display quarantine UI.  That's bad,
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # especially if it happens when the outer .app launches a quarantined inner
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # helper.  If the application is already on the system and is being updated,
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # then it can be assumed that it should not be quarantined.  Use xattr to
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # drop the quarantine attribute.
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # TODO(mark): Instead of letting the quarantine attribute be set and then
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # dropping it here, figure out a way to get the update process to run
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # without LSFileQuarantineEnabled even when triggering an update from within
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the application.
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "lifting quarantine"
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if os_xattr_supports_r; then
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # On 10.6, xattr supports -r for recursive operation.
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    xattr -d -r "${QUARANTINE_ATTR}" "${installed_app}" 2> /dev/null
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # On earlier systems, xattr doesn't support -r, so run xattr via find.
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    find "${installed_app}" -exec xattr -d "${QUARANTINE_ATTR}" {} + \
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        2> /dev/null
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Do Keychain reauthorization. This involves running a stub executable on
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the dmg that loads the newly-updated framework and jumps to it to perform
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the reauthorization. The stub executable can be signed by the old
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # certificate even after the rest of Chrome switches to the new certificate,
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # so it still has access to the old Keychain items. The stub executable is
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # an unbundled flat file executable whose name matches the real
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # application's bundle identifier, so it's permitted access to the Keychain
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # items. Doing a reauthorization step at update time reauthorizes Keychain
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # items for users who never bother restarting Chrome, and provides a
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # mechanism to continue doing reauthorizations even after the certificate
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # changes. However, it only works for non-system ticket installations of
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Chrome, because the updater runs as root when on a system ticket, and root
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # can't access individual user Keychains.
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Even if the reauthorization tool is launched, it doesn't necessarily try
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to do anything. It will only attempt to perform a reauthorization if one
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # hasn't yet been done at update time.
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "maybe reauthorizing Keychain"
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if [[ -z "${system_ticket}" ]]; then
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local new_bundleid_app
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_bundleid_app="$(infoplist_read "${installed_app_plist}" \
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       "${APP_BUNDLEID_KEY}" || true)"
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "new_bundleid_app = ${new_bundleid_app}"
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local keychain_reauthorize_dir="\
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)${update_dmg_mount_point}/${KEYCHAIN_REAUTHORIZE_DIR}"
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    local keychain_reauthorize_path="\
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)${keychain_reauthorize_dir}/${new_bundleid_app}"
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "keychain_reauthorize_path = ${keychain_reauthorize_path}"
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if [[ -x "${keychain_reauthorize_path}" ]]; then
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local framework_dir="${new_versioned_dir}/${FRAMEWORK_DIR}"
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      local framework_code_path="${framework_dir}/${FRAMEWORK_NAME}"
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      note "framework_code_path = ${framework_code_path}"
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if [[ -f "${framework_code_path}" ]]; then
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        note "reauthorizing Keychain"
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "${keychain_reauthorize_path}" "${framework_code_path}"
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      fi
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fi
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    note "system ticket, not reauthorizing Keychain"
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  fi
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # Great success!
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  note "done!"
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  trap - EXIT
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Check "less than" instead of "not equal to" in case Keystone ever changes to
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# pass more arguments.
14085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if [[ ${#} -lt 1 ]]; then
14095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  usage
14105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  exit 2
14115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)fi
14125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)main "${@}"
14145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)exit ${?}
1415