blob: d62f706ef69adaaa897d4a6b0bba312d99eba61d [file] [log] [blame]
#!/bin/bash
# Copyright (c) 2011 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 force_latest "${FLAGS_FALSE}" \
"Assume latest version and recreate the version file"
DEFINE_boolean skipfirst "${FLAGS_FALSE}" \
"Skip the first upgrade. This may be dangerous."
FLAGS "$@" || exit 1
VERSION_FILE=/etc/cros_chroot_version
UPGRADE_D="$(dirname ${0})/chroot_version_hooks.d"
update_version() {
sudo touch ${VERSION_FILE}
sudo chown ${USER} ${VERSION_FILE}
echo "${1}" > "${VERSION_FILE}"
}
######################################################################
# Sanity checks:
if [ -n "${FLAGS_version}" ] && \
( [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] || \
[ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ] ); then
error "The option --version cannot be combined with either"
error "--skipfirst or --force_latest."
exit 1
fi
if [ "${FLAGS_skipfirst}" == "${FLAGS_TRUE}" ] &&
[ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ]; then
error "--skipfirst and --force_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=$(
ls "${UPGRADE_D}" | grep "^[0-9]*_" | \
sort -n | tail -n 1 | cut -f1 -d'_'
)
if [ "${FLAGS_force_latest}" == "${FLAGS_TRUE}" ]; then
update_version "${LATEST_VERSION}"
exit 0
fi
# If the file does not exist at all, chroot is old and does not have a version.
# default goes here
if ! [ -f "${VERSION_FILE}" ]; then
info "Chroot of unknown version, initializing to 0"
update_version 0
fi
CHROOT_VERSION=$(cat "${VERSION_FILE}")
# Check if version is a number.
if ! [ "${CHROOT_VERSION}" -ge "0" ] &> /dev/null; then
error "Your chroot version file ${VERSION_FILE} is bogus: ${CHROOT_VERSION}"
exit 1
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 "${UPGRADE_D}" 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)
# Side effect is that the scripts have to be internally enclosed in
# a code block, otherwise simply running "exit" in any of them would
# terminate the master script, so we call it in a subshell.
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
fi