#!/bin/sh
# $FiG: config/freebsd/builder01/root/bin/create-and-populate-new-BE.sh,v 1.7 2018-07-30 06:52:11 trond Exp $
# $BSDnet: config/freebsd/enterprise/root/bin/create-and-populate-new-BE.sh,v 1.3 2018-07-30 08:17:53 trond Exp $
#set -x
set -o pipefail

# Specify which kernel to install.
#KERNCONF=GENERIC
#KERNCONF=ENTERPRISE
KERNCONF=`sysctl -n kern.ident`

# Specify where to place the typescripts.
TYPESCRIPT_DIR=/mnt/root/tmp
#TYPESCRIPT_DIR=/usr/src
#TYPESCRIPT_DIR=/var/log/buildlog

# Specify where the bootpool BE is mounted, typically /bootpool.
BOOTPOOL_ROOT=/bootpool

# Specify where to temporary mount the new BEs, typically /mnt.
DESTDIR=/mnt

# Determine a suitable git executable.
GIT_CANDIDATES="/usr/local/bin/git /usr/bin/git /usr/bin/gitlite"

for cand in ${GIT_CANDIDATES}; do
  if [ -x ${cand} ]; then
    GIT=${cand}
    break
  fi
done

if [ -z "${GIT}" ]; then
  echo "${0}: neither of ${GIT_CANDIDATES} are useable" >/dev/stderr
  exit 69
fi

# Ensure / is of type ZFS.
if [ -z "`/bin/df -Tt zfs /`" ]; then
  echo "${0}: root filesystem must be of type ZFS" >/dev/stderr
  exit 69
fi

# Ensure ${BOOTPOOL_ROOT}/boot/loader.conf exists.
if [ ! -f ${BOOTPOOL_ROOT}/boot/loader.conf ]; then
  echo "${0}: ${BOOTPOOL_ROOT}/boot/loader.conf does not exist" >/dev/stderr
  exit 69
fi

# Ensure /usr/src is mounted, be it NFS or local fs.
if [ ! -f /usr/src/Makefile ]; then
  /sbin/mount /usr/src

  if [ ! -f /usr/src/Makefile ]; then
    echo "${0}: unable to mount /usr/src" >/dev/stderr
    exit 69
  fi
fi

# Ensure /usr/obj is mounted, be it NFS or local fs.
if [ ! -d /usr/obj/usr ]; then
  /sbin/mount /usr/obj

  if [ ! -d /usr/obj/usr ]; then
    echo "${0}: unable to mount /usr/obj" >/dev/stderr
    exit 69
  fi
fi

# What's the name of the current boot pool BE?
CURRENT_BOOTPOOL_BE=`/bin/df ${BOOTPOOL_ROOT} | /usr/bin/tail -1 | /usr/bin/awk '{print $1}'`
if [ -z "${CURRENT_BOOTPOOL_BE}" ]; then
  echo "${0}: zero length CURRENT_BOOTPOOL_BE" >/dev/stderr
  exit 69
fi

# What's the name of the current root pool BE?
CURRENT_ROOTPOOL_BE=`/bin/df / | /usr/bin/tail -1 | /usr/bin/awk '{print $1}'`
if [ -z "${CURRENT_ROOTPOOL_BE}" ]; then
  echo "${0}: zero length CURRENT_ROOTPOOL_BE" >/dev/stderr
  exit 69
fi

# What's the current timestamp given local time?
CURRENT_TIMESTAMP=`/bin/date +%Y%m%d-%H%M%S`
if [ -z "${CURRENT_TIMESTAMP}" ]; then
  echo "${0}: zero length CURRENT_TIMESTAMP" >/dev/stderr
  exit 69
fi

# What's the Git commit count and the latest Git commit hash?
echo "Inquiring Git about branch name, commit count, and latest commit hash ..."
GIT_BRANCH=`${GIT} -C /usr/src rev-parse --abbrev-ref HEAD | sed 's|[/+]|-|g'`
GIT_COMMIT_COUNT=`${GIT} -C /usr/src rev-list --first-parent --count HEAD`
GIT_SHORT_COMMIT_HASH=`${GIT} -C /usr/src rev-parse --verify --short=12 HEAD`
echo "... done"

# Construct the new identification.
NEW_ID="${CURRENT_TIMESTAMP}-${GIT_BRANCH}-n${GIT_COMMIT_COUNT}-${GIT_SHORT_COMMIT_HASH}"

# Construct the name of the root pool snapshot.
NEW_SNAPSHOT="${CURRENT_ROOTPOOL_BE}@${NEW_ID}"

# Construct the name of the boot pool snapshot.
NEW_BOOTPOOL_SNAPSHOT="${CURRENT_BOOTPOOL_BE}@${NEW_ID}"

# Get the root of the boot pool BEs.
BOOTPOOL_BE_ROOT=`/usr/bin/dirname ${CURRENT_BOOTPOOL_BE}`
if [ -z "${BOOTPOOL_BE_ROOT}" ]; then
  echo "${0}: zero length BOOTPOOL_BE_ROOT" >/dev/stderr
  exit 69
fi

# Get the name of the boot pool.
BOOT_POOL=`echo "${BOOTPOOL_BE_ROOT}" | /usr/bin/cut -d / -f 1`
if [ -z "${BOOT_POOL}" ]; then
  echo "${0}: zero length BOOT_POOL" >/dev/stderr
  exit 69
fi

# Construct the name of the new boot pool BE.
NEW_BOOTPOOL_BE="${BOOTPOOL_BE_ROOT}/${NEW_ID}"

# Construct the name of the root pool snapshot.
NEW_ROOTPOOL_SNAPSHOT="${CURRENT_ROOTPOOL_BE}@${NEW_ID}"

# Get the root of the root pool BEs.
ROOTPOOL_BE_ROOT=`/usr/bin/dirname ${CURRENT_ROOTPOOL_BE}`
if [ -z "${ROOTPOOL_BE_ROOT}" ]; then
  echo "${0}: zero length ROOTPOOL_BE_ROOT" >/dev/stderr
  exit 69
fi

# Get the name of the root pool.
ROOT_POOL=`echo "${ROOTPOOL_BE_ROOT}" | /usr/bin/cut -d / -f 1`
if [ -z "${ROOT_POOL}" ]; then
  echo "${0}: zero length ROOT_POOL" >/dev/stderr
  exit 69
fi

# Construct the name of the new root pool BE.
NEW_ROOTPOOL_BE="${ROOTPOOL_BE_ROOT}/${NEW_ID}"

# Run pre-flight checks. This is an interactive step.
#/usr/sbin/mergemaster -Fp || exit
if [ ! -d /var/db/etcupdate/current ]; then
  /usr/sbin/etcupdate extract || exit

  if [ ! -d /var/db/etcupdate/current ]; then
    echo "${0}: unable to run etcupdate(8), missing directory /var/db/etcupdate/current" >/dev/stderr
    exit 69
  fi
fi

echo "Running pre-flight checks using etcupdate(8) ..."
/usr/sbin/etcupdate         -p || exit
/usr/sbin/etcupdate resolve -p || exit
echo "... done."

# Create the root pool snapshot and the new root pool BE.
/sbin/zfs snapshot ${NEW_ROOTPOOL_SNAPSHOT} || exit
/sbin/zfs clone -o mountpoint=${DESTDIR} ${NEW_ROOTPOOL_SNAPSHOT} ${NEW_ROOTPOOL_BE} || exit

# Create the boot pool snapshot and the new boot pool BE.
/sbin/zfs snapshot ${NEW_BOOTPOOL_SNAPSHOT} || exit
/sbin/zfs clone -o mountpoint=${DESTDIR}${BOOTPOOL_ROOT} ${NEW_BOOTPOOL_SNAPSHOT} ${NEW_BOOTPOOL_BE} || exit

# Update vfs.root.mountfrom in ${DESTDIR}${BOOTPOOL_ROOT}/boot/loader.conf to point to the new root pool BE.
/usr/bin/sed -i '' "s|^vfs\\.root\\.mountfrom=\".*\"\$|vfs.root.mountfrom=\"zfs:${NEW_ROOTPOOL_BE}\"|" ${DESTDIR}${BOOTPOOL_ROOT}/boot/loader.conf || exit

# Merge any changes to the configuration files. This is an interactive step.
#/usr/sbin/mergemaster -D ${DESTDIR} -Fi || exit
echo "Merging changes to configuration files using etcupdate(8) ..."
/usr/sbin/etcupdate         -D ${DESTDIR} -d /var/db/etcupdate || exit
/usr/sbin/etcupdate resolve -D ${DESTDIR} -d /var/db/etcupdate || exit
while /usr/sbin/etcupdate status -D ${DESTDIR} -d /var/db/etcupdate | grep -q '^  C'; do
  /usr/sbin/etcupdate resolve -D ${DESTDIR} -d /var/db/etcupdate || exit
done
echo "... done."

# Ensure we have somewhere to place the typescripts.
if [ ! -d ${TYPESCRIPT_DIR} ]; then
  /bin/mkdir -p ${TYPESCRIPT_DIR}

  if [ ! -d ${TYPESCRIPT_DIR} ]; then
    echo "${0}: unable to create ${TYPESCRIPT_DIR}" >/dev/stderr
    exit 69
  fi
fi

# Remove previous typescripts.
/bin/rm -f -- ${TYPESCRIPT_DIR}/make-installworld-installkernel-*.txt ${TYPESCRIPT_DIR}/make-check-old-*.txt ${TYPESCRIPT_DIR}/make-delete-old-*.txt

# Install new world and kernel into the new BE.
/usr/bin/script -at 0 ${TYPESCRIPT_DIR}/make-installworld-installkernel-${NEW_ID}.txt /usr/bin/make -C /usr/src -j `/sbin/sysctl -n hw.ncpu` DESTDIR=${DESTDIR} KERNCONF=${KERNCONF} installworld installkernel || exit

# Check to see if there's something old and outdated.
/usr/bin/script -at 0 ${TYPESCRIPT_DIR}/make-check-old-${NEW_ID}.txt /usr/bin/make -C /usr/src DESTDIR=${DESTDIR} check-old || exit

# Remove any old files.
/usr/bin/script -at 0 ${TYPESCRIPT_DIR}/make-delete-old-${NEW_ID}.txt /usr/bin/make -C /usr/src -D BATCH_DELETE_OLD_FILES DESTDIR=${DESTDIR} delete-old || exit

# Remove any old libraries.
/usr/bin/script -at 0 ${TYPESCRIPT_DIR}/make-delete-old-libs-${NEW_ID}.txt /usr/bin/make -C /usr/src -D BATCH_DELETE_OLD_FILES DESTDIR=${DESTDIR} delete-old-libs || exit

# Check to see if there's still something old and outdated.
/usr/bin/script -at 0 ${TYPESCRIPT_DIR}/make-check-old-${NEW_ID}.txt /usr/bin/make -C /usr/src DESTDIR=${DESTDIR} check-old || exit

# Unmount the NFS mounted filesystems if needed.
if [ "`/bin/df -T /usr/src | /usr/bin/tail -1 | /usr/bin/awk '{print $2}'`" = "nfs" ]; then
  /sbin/umount /usr/src || exit
fi
if [ "`/bin/df -T /usr/obj | /usr/bin/tail -1 | /usr/bin/awk '{print $2}'`" = "nfs" ]; then
  /sbin/umount /usr/obj || exit
fi

# Unconditionally remove any traces of any (false) NFS mounts.
/bin/rm -f -- ${DESTDIR}/var/db/mounttab

# Enable this if you have bemigrate.sh stored as /etc/rc.d/bemigrate
# and also have set bemigrate_enable="YES" in /etc/rc.conf.
# Place the /firstboot marker in the new BE.
/usr/bin/touch ${DESTDIR}/firstboot

# Inherit the mountpoint for the current boot pool BE.
/sbin/zfs inherit mountpoint ${CURRENT_BOOTPOOL_BE} || exit

# Update the mountpoint for the new boot pool BE.
/sbin/zfs set mountpoint=${BOOTPOOL_ROOT} ${NEW_BOOTPOOL_BE} || exit

# Set the bootfs zpool property on the boot pool.
/sbin/zpool set bootfs=${NEW_BOOTPOOL_BE} ${BOOT_POOL} || exit

# Inherit the mountpoint for the new root pool BE.
/sbin/zfs inherit mountpoint ${NEW_ROOTPOOL_BE} || exit

# Ready to reboot into the new boot pool BE.
echo "${0}: ready to boot into ${NEW_BOOTPOOL_BE}"

# EOF
