#!/bin/sh
# Copyright (c) 2017, 2021 Jonas 'Sortie' Termansen.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# tix-upgrade
# Upgrade operating system and ports.

set -e

cachedir=""
clean=false
collection=""
download_only=false
fetch_options=
ports_only=false
sysroot=""
upgrade_ports=false
upgrade_system=false
wait=""

dashdash=
previous_option=
for argument do
  if test -n "$previous_option"; then
    eval $previous_option=\$argument
    previous_option=
    shift
    continue
  fi

  case $argument in
  *=?*) parameter=$(expr "X$argument" : '[^=]*=\(.*\)' || true) ;;
  *=)   parameter= ;;
  *)    parameter=yes ;;
  esac

  case $dashdash$argument in
  --) dashdash=yes ;;
  -w) wait=-w ;;
  --cachedir=*) cachedir=$parameter ;;
  --cachedir) previous_option=cachedir ;;
  --clean) clean=true ;;
  --collection=*) collection=$parameter ;;
  --collection) previous_option=collection ;;
  --download-only) download_only=true ;;
  --insecure-downgrade-to-http) fetch_options="$fetch_options $argument" ;;
  --insecure-no-check-certificate) fetch_options="$fetch_options $argument" ;;
  --ports) upgrade_ports=true ;;
  --system) upgrade_system=true ;;
  --sysroot) previous_option=sysroot ;;
  --sysroot=*) sysroot=$parameter ;;
  --wait) wait=--wait ;;
  -*) echo "$0: unrecognized option $argument" >&2
      exit 1 ;;
  *) break ;;
  esac

  shift
done

if test -n "$previous_option"; then
  echo "$0: option '$argument' requires an argument" >&2
  exit 1
fi

# TODO: Reject additional operands.

if [ -z "$collection" ]; then
  collection="$sysroot"
fi

if [ -n "$collection" ]; then
  collection=$(cd "$collection" && pwd)
fi

if ! $upgrade_ports && ! $upgrade_system; then
  upgrade_ports=true
  upgrade_system=true
fi

# Update only ports if this isn't a whole system installation.
if [ ! -e "$collection/tix/manifest/system" ]; then
  ports_only=true
fi

if [ -z "$cachedir" ]; then
  cachedir="$collection/var/cache/tix"
fi

if $clean; then
  echo "Removing cache directory: $cachedir"
  rm -rf -- "$cachedir"
fi

mkdir -p -- "$cachedir"
mkdir -p -- "$cachedir/new"

# Fetch latest release.sig.sh from the master and its matching sha256sum file.
tix-fetch $fetch_options \
          --output-release-sig-file="$cachedir/new/release.sh.sig" \
          --output-sha256sum="$cachedir/new/sha256sum"

# If release.sig.sh or sha256sum changed, clean the cache directory of downloads
# that were currently in progress as they might not have the right checksums.
# TODO: This requires diff(1) in the base system! Avoid that. Get a cmp(1)!
if ! diff -- "$cachedir/release.sh.sig" \
             "$cachedir/new/release.sh.sig" 1>/dev/null 2>/dev/null ||
   ! diff -- "$cachedir/sha256sum" \
             "$cachedir/new/sha256sum" 1>/dev/null 2>/dev/null; then
  rm -rf -- "$cachedir/boot"
  rm -rf -- "$cachedir/repository"
  rm -rf -- "$cachedir/sysroot"
fi

# Store the new release.sig.sh and sha256sum files so we can resume the download
# if cancelled and these files still match.
mv -- "$cachedir/new/release.sh.sig" "$cachedir/release.sh.sig"
mv -- "$cachedir/new/sha256sum" "$cachedir/sha256sum"
rm -rf -- "$cachedir/new"

mkdir -p "$cachedir/boot"
mkdir -p "$cachedir/repository"

if $upgrade_system; then
  # Fetch the base system initrds from the mirror.
  # TODO: What about the system source code in /src?
  # TODO: Get the overlay initrd if it exists.
  tix-fetch $fetch_options \
            --input-release-sig-file="$cachedir/release.sh.sig" \
            --input-sha256sum="$cachedir/sha256sum" \
            -c --initrd -O "$cachedir/boot" -- system
fi

if $upgrade_ports; then
  # Fetch each port from the mirror.
  ports=$(LC_ALL=C sort -- "$collection/tix/installed.list")
  # TODO: This fails if the port does not exist upstream.
  mkdir -p "$cachedir/repository"
  for port in $ports; do
    # The port has a hash if if it exists upstream.
    sha256=$(tix-fetch $fetch_options \
                       --input-release-sig-file="$cachedir/release.sh.sig" \
                       --input-sha256sum="$cachedir/sha256sum" \
                       --sha256 --port -- $port)
    # If the port exists upstream, get the latest version.
    if [ -n "$sha256" ]; then
      tix-fetch $fetch_options \
                --input-release-sig-file="$cachedir/release.sh.sig" \
                --input-sha256sum="$cachedir/sha256sum" \
                -c --port -O "$cachedir/repository" -- $port
    fi
  done
fi

# Stop if only downloading.
if $download_only; then
  exit
fi

rm -rf -- "$cachedir/sysroot"
mkdir -p -- "$cachedir/sysroot"

if $upgrade_system; then
  # Extract the base system into the sysroot.
  rm -rf -- "$cachedir/sysroot"
    mkdir -p -- "$cachedir/sysroot"
  for initrd in system overlay; do
    initrd_path="$cachedir/boot/$initrd.initrd"
    if [ -e "$initrd_path.xz" ]; then
      echo "Extracting $initrd.initrd.xz..."
      unxz -k "$initrd_path.xz"
      initrdfs "$initrd_path" extract -C "$cachedir/sysroot" /
      rm -f "$initrd_path.xz"
    fi
  done
fi

# TODO: These install all the ports in "$cachedir/repository" but the directory
#       might not have been cleared, it could contain unverified ports.
if $upgrade_system; then
  # Do a full system upgrade using the sysroot and the new ports.
  # TODO: If sysmerge -w, such as on ABI upgrades, the e2fsprogs package is not
  #       installed in the system root and the new initrd might not contain a
  #       /sbin/fsck.ext2 and the system can become bricked if it needs a fsck
  #       on boot.
  # TODO: There's no support here for doing a sysmerge on a given sysroot.
  if $upgrade_ports; then
    # TODO: Get the architecture from somewhere else than /etc/machine?
    tix-collection "$cachedir/sysroot" create \
      --platform="$(cat -- "$sysroot/etc/machine")-sortix" \
      --prefix= --generation=2
    tix-install --collection="$cachedir/sysroot" -- "$cachedir/repository/"*
    full=--full
  else
    # TODO: Is this case supported?
    full=
  fi
  sysmerge $full $wait "$cachedir/sysroot"
elif $upgrade_ports; then
  # Upgrade to each of the new ports.
  # TODO: tix-install --reinstall is unsupported due to lack of support for
  #       files moving between manifests or being deleted.
  tix-install --reinstall -- "$cachedir/repository/"*
fi

rm -rf -- "$cachedir/boot"
rm -rf -- "$cachedir/repository"
rm -rf -- "$cachedir/sysroot"

#tmp=
#trap '[ -n "$tmp" ] && rm -rf "$tmp"' EXIT HUP INT QUIT TERM
#tmp=$(mktemp -dt upgrade.XXXXXX)
#mkdir -p "$tmp/tmp"
#export TMPDIR="$tmp/tmp"
#(cd "$tmp" && tix-fetch --release -o sortix.iso)
#mkdir -p "$tmp/sortix.iso.d"
#echo "Extracting sortix.iso..."
#xorriso -osirrox on -indev "$tmp/sortix.iso" -extract / "$tmp/sortix.iso.d" > /dev/null 2> /dev/null
#rm -f "$tmp/sortix.iso"
