| #!/bin/bash |
| |
| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Load common CrOS utilities. Inside the chroot this file is installed in |
| # /usr/lib/crosutils. Outside the chroot we find it relative to the script's |
| # location. |
| . "$(dirname "$0")/common.sh" || exit 1 |
| |
| # Script must run inside the chroot. |
| assert_inside_chroot |
| |
| # May not be run as root. |
| assert_not_root_user |
| |
| DEFINE_string version "" \ |
| "Assume current chroot version is this." |
| DEFINE_boolean init_latest "${FLAGS_FALSE}" \ |
| "Create the version file with the latest version if it doesn't exist" |
| DEFINE_boolean skipfirst "${FLAGS_FALSE}" \ |
| "Skip the first upgrade. This may be dangerous." |
| |
| FLAGS "$@" || exit 1 |
| |
| VERSION_HOOKS_DIR="$(dirname "$(readlink -f "${0}")")/chroot_version_hooks.d" |
| |
| update_version() { |
| sudo touch "${CHROOT_VERSION_FILE}" |
| sudo chown "${USER}" "${CHROOT_VERSION_FILE}" |
| echo "${1}" > "${CHROOT_VERSION_FILE}" |
| } |
| |
| ###################################################################### |
| |
| # Sanity checks: |
| if [ -n "${FLAGS_version}" ] && \ |
| ( [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] || \ |
| [ "${FLAGS_init_latest}" == "${FLAGS_TRUE}" ] ); then |
| error "The option --version cannot be combined with either" |
| error "--skipfirst or --init_latest." |
| exit 1 |
| fi |
| |
| if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] && |
| [ "${FLAGS_init_latest}" == "${FLAGS_TRUE}" ]; then |
| error "--skipfirst and --init_latest cannot be combined." |
| exit 1 |
| fi |
| |
| # Latest version is the version of last upgrade.d file. |
| # Name format is ${number}_${short_description} |
| # Versions must be -n sorted, that is, the first continuous sequence |
| # of numbers is what counts. 12_ is before 111_, etc. |
| LATEST_VERSION=$( |
| cd "${VERSION_HOOKS_DIR}" |
| ls [0-9]*_* | cut -d_ -f1 | sort -rn | head -n1) |
| |
| # If the file does not exist at all, chroot is old and does not have a version. |
| # default goes here |
| if ! [ -f "${CHROOT_VERSION_FILE}" ]; then |
| update_version 0 |
| fi |
| |
| CHROOT_VERSION=$(<"${CHROOT_VERSION_FILE}") |
| # Check if version is a number. |
| if ! [ "${CHROOT_VERSION}" -ge "0" ] &> /dev/null; then |
| error "Your chroot version file ${CHROOT_VERSION_FILE} is bogus: " |
| error "${CHROOT_VERSION}" |
| exit 1 |
| fi |
| |
| if [[ "${FLAGS_init_latest}" == "${FLAGS_TRUE}" ]]; then |
| if [[ "${CHROOT_VERSION}" -eq "0" ]]; then |
| info "Initializing chroot to version ${LATEST_VERSION}" |
| update_version "${LATEST_VERSION}" |
| else |
| info "Chroot is already initialized to ${CHROOT_VERSION}" |
| fi |
| exit 0 |
| fi |
| |
| if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ]; then |
| if [ "${CHROOT_VERSION}" -lt "${LATEST_VERSION}" ]; then |
| # if the new one is latest, this becomes noop |
| CHROOT_VERSION=$(expr ${CHROOT_VERSION} + 1) |
| update_version "${CHROOT_VERSION}" |
| else |
| error "Nothing to skip" |
| exit 1 |
| fi |
| fi |
| |
| if [ -n "${FLAGS_version}" ]; then |
| # Check if it's a number. |
| if ! [ "${FLAGS_version}" -ge "0" ] &> /dev/null; then |
| error "Trying to force invalid version: ${FLAGS_version}" |
| exit 1 |
| fi |
| |
| if [ "${FLAGS_version}" -gt "${LATEST_VERSION}" ]; then |
| error "Forcing nonexistant version: ${FLAGS_version}" |
| exit 1 |
| fi |
| |
| CHROOT_VERSION="${FLAGS_version}" |
| fi |
| |
| |
| if [ "${LATEST_VERSION}" -gt "${CHROOT_VERSION}" ]; then |
| info "Old chroot version (${CHROOT_VERSION}) found, running upgrade hooks" |
| |
| pushd "${VERSION_HOOKS_DIR}" 1> /dev/null |
| for n in $(seq "$(expr ${CHROOT_VERSION} + 1)" "${LATEST_VERSION}"); do |
| hook=(${n}_*) |
| |
| # Sanity check: if there are multiple ${n}_* files, then CL's landed |
| # at the same time and people didn't notice. Let's notice for them. |
| if [ ${#hook[@]} -gt 1 ]; then |
| error "Fatal: Upgrade ${n} has multiple hooks:" |
| error " ${hook[*]}" |
| error "Connor MacLeod knows: There can be only one." |
| exit 1 |
| fi |
| hook=${hook[0]} |
| |
| # Deprecation check; Deprecation can be done by removing old upgrade |
| # scripts and causing too old chroots to have to start over. |
| # Upgrades have to form a continuous sequence. |
| if ! [ -f ${hook} ]; then |
| error "Fatal: Upgrade ${n} doesn't exist." |
| error "Your chroot is so old, that some updates have been deprecated!" |
| error "You need to re-create it!" |
| exit 1 |
| fi |
| |
| info "Rollup ${hook}" |
| |
| # Attempt the upgrade. |
| # NOTE: We source the upgrade scripts because: |
| # 1) We can impose set -something on them. |
| # 2) They can reuse local variables and functions (fe. from common.sh) |
| # 3) They're allowed to use VERSION_HOOKS_DIR and CHROOT_VERSION_FILE. |
| # Note that the upgrade scripts have to be subshelled to protect ourselves, |
| # else a script running exit would stop the upgrade process entirely. |
| if ! ( source ${hook} ); then |
| error "Fatal: failed to upgrade ${n}!" |
| exit 1 |
| fi |
| # Each upgrade is atomic. If a middle upgrade fails, we won't retry |
| # all the ones that passed on a previous run. |
| update_version "${n}" |
| done |
| popd 1> /dev/null |
| elif [ "${LATEST_VERSION}" -lt "${CHROOT_VERSION}" ]; then |
| error "Fatal: Missing upgrade hook for ${CHROOT_VERSION}" |
| error "Chroot version is too new. Consider running cros_sdk --replace" |
| exit 1 |
| fi |
| |
| command_completed |