#!/bin/bash
# linaro-hwpack-install - Install a Linaro Hardware Pack.
#   This script is meant to run inside a chroot containing nothing other than
#   ubuntu-minimal, so it must not depend on anything that's not in
#   there.
# TODO: When upgrading to a newer hwpack, make sure packages and apt sources
# that are no longer needed are removed.

# Copyright (C) 2010, 2011 Linaro
#
# Author: Guilherme Salgado <guilherme.salgado@linaro.org>
#
# This file is part of Linaro Image Tools.
#
# Linaro Image Tools is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# Linaro Image Tools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with Linaro Image Tools; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
# USA.

set -e

LOCKFILE="/var/lock/hwpack"
TEMP_DIR=$(mktemp -d)
HWPACK_DIR="${TEMP_DIR}/unpacked"
INSTALL_LATEST="no"
FORCE_YES="no"
SOURCES_LIST_FILE="${TEMP_DIR}/sources.list"
APT_GET_OPTIONS="Dir::Etc::SourceList=${SOURCES_LIST_FILE}"
SUPPORTED_FORMATS="1.0 2.0"  # A space-separated list of hwpack formats.

sudo="sudo"
if [ $(id -u) -eq 0 ]; then
    sudo=""
fi

die() {
  echo -e "$@"
  exit 1
}

usage_msg="Usage: $(basename $0) [--install-latest] [--force-yes] HWPACK_TARBALL"
if [ $# -eq 0 ]; then
  die $usage_msg
fi

HWPACK_TARBALL_FOUND="no"

while [ $# -gt 0 ]; do
  case "$1" in 
    --install-latest)
      INSTALL_LATEST="yes"
      shift;;
    --force-yes)
      FORCE_YES="yes"
      shift;;
    --*)
      die $usage_msg "\nUnrecognized option: \"$1\"";;
    *)
      [ "$HWPACK_TARBALL_FOUND" = "yes" ] && die $usage_msg
      HWPACK_TARBALL="$1"
      HWPACK_TARBALL_FOUND="yes"
      shift;;
  esac
done

[ "$HWPACK_TARBALL_FOUND" = "no" ] && die $usage_msg

# Try to acquire fd #9 (i.e. /var/lock/hwpack) for 2 seconds.
# Using 9 as the file descriptor because of https://launchpad.net/bugs/249620
exec 9>$LOCKFILE
flock -w2 9 || die "Could not acquire lock: $LOCKFILE"

cleanup() {
  # Ensure our temp dir and apt sources are removed.
  echo -n "Cleaning up ..."
  rm -rf $TEMP_DIR
  $sudo apt-get update -qq
  echo "Done"
}

# From now on we'll be making changes to the system, so we need to clean
# things up when the script exits.
trap cleanup EXIT

# This creates all the directories we need.
mkdir -p "$HWPACK_DIR"

# Unpack the hwpack tarball. We don't download it here because the chroot may
# not contain any tools that would allow us to do that.
echo -n "Unpacking hardware pack ..."
tar zxf "$HWPACK_TARBALL" -C "$HWPACK_DIR"
echo "Done"

# Check the format of the hwpack is supported.
hwpack_format=$(cat ${HWPACK_DIR}/FORMAT)
supported="false"
for format in $SUPPORTED_FORMATS; do
  if [ $hwpack_format == $format ]; then
    supported="true"
    break
  fi
done
[ $supported == "true" ] || \
  die "Unsupported hwpack format: $hwpack_format. "\
      "Try using a newer version of $(basename $0)."

# Check the architecture of the hwpack matches that of the host system.
HWPACK_ARCH=`grep ARCHITECTURE "${HWPACK_DIR}/metadata" | cut -d "=" -f2`
[ "$HWPACK_ARCH" == `dpkg --print-architecture` ] || \
  die "Hardware pack architecture ($HWPACK_ARCH) does not match the host's architecture"

# Install the apt sources that contain the packages we need.
for filename in $(ls "${HWPACK_DIR}"/sources.list.d/); do
  file="${HWPACK_DIR}"/sources.list.d/$filename
  should_install=0
  stripped_file=${TEMP_DIR}/$filename
  grep -v "\(^#\|^\s*$\)" $file > $stripped_file
  while read line; do
    # Only install files that have at least one line not present in the
    # existing sources lists.
    grep -qF "$line" /etc/apt/sources.list.d/* /etc/apt/sources.list \
      || should_install=1
  done < $stripped_file

  if [ $should_install -eq 1 ]; then
    $sudo cp $file /etc/apt/sources.list.d/hwpack.$filename
  fi
done

# Import the OpenPGP keys for the files installed above.
for filename in $(ls "${HWPACK_DIR}"/sources.list.d.gpg/); do
  file="${HWPACK_DIR}"/sources.list.d.gpg/$filename
  $sudo apt-key add $file
done

# Add one extra apt source for the packages included in the hwpack and make
# sure it's the first on the list of sources so that it gets precedence over
# the others.
echo "deb file:${HWPACK_DIR}/pkgs ./" > "$SOURCES_LIST_FILE"
cat /etc/apt/sources.list >> "$SOURCES_LIST_FILE"

echo "Updating apt package lists ..."
$sudo apt-get -o "$APT_GET_OPTIONS" update -q

echo -n "Installing packages ..."

if [ "$FORCE_YES" == "yes" ]; then
  FORCE_OPTIONS="--yes --force-yes"
else
  FORCE_OPTIONS=""
fi

# "newer" hwpacks contain a dependency package whose Depends is the
# same as the packages config setting from the file the hwpack was
# build from.  But if we just installed that, a newer version of a
# package than that in the hwpack might have made it to the main
# archive and apt-get would install that instead.  So we install the
# specific package versions that make up the hwpack.  /That/ however
# would leave all the packages from the hwpack marked as manually
# installed, so if a newer hwpack was installed over the top which no
# longer depended on one of the packages the older one did, the
# package would not be eligible for autoremoval.  So we mark the all
# packages newly installed as part of hwpack installed (apart from the
# dependency package) as automatically installed with apt-get
# markauto.
#
# For "older" hwpacks that don't have a dependency package, we just
# manually install the contents of the hwpack.

HWPACK_NAME=`grep NAME "${HWPACK_DIR}/metadata" | cut -d "=" -f2`
HWPACK_VERSION=`grep VERSION "${HWPACK_DIR}/metadata" | cut -d "=" -f2`
dependency_package="hwpack-${HWPACK_NAME}"
if grep -q "^${dependency_package}=${HWPACK_VERSION}\$" "${HWPACK_DIR}"/manifest; then
  DEP_PACKAGE_PRESENT="yes"
else
  DEP_PACKAGE_PRESENT="no"
fi

packages_without_versions=`sed 's/=.*//' "${HWPACK_DIR}"/manifest`
packages_with_versions=`cat "${HWPACK_DIR}"/manifest`

if [ "$INSTALL_LATEST" == "yes" ]; then
  packages="${packages_without_versions}"
else
  packages="${packages_with_versions}"
fi

if [ "$DEP_PACKAGE_PRESENT" == "yes" ]; then
  to_be_installed=
  for package in $packages_without_versions; do
    if [ "${package}" != "${dependency_package}" ]; then
      { dpkg --get-selections $package 2>/dev/null| grep -qw 'install$'; } || to_be_installed="$to_be_installed $package"
    fi
  done
fi

$sudo apt-get $FORCE_OPTIONS -o "$APT_GET_OPTIONS" install ${packages}

if [ "$DEP_PACKAGE_PRESENT" == "yes" ]; then
  if [ -n "${to_be_installed}" ]; then
    $sudo apt-get $FORCE_OPTIONS -o "$APT_GET_OPTIONS" markauto ${to_be_installed}
  fi
fi

echo "Done"
