#!/bin/sh
BE_UTILITY="${BE_UTILITY:-"bectl"}"
NO_PKG_UPGRADE="${NO_PKG_UPGRADE:-""}"
CONFIG_UPDATER="${CONFIG_UPDATER:-"etcupdate"}"
ETCUPDATE_FLAGS="${ETCUPDATE_FLAGS:-"-BF"}"
cleanup() {
[ -z "${cleanup_commands}" ] && return
echo "Cleaning up ..."
for command in ${cleanup_commands}; do
${command}
done
}
errx() {
cleanup
echo "error: $@"
exit 1
}
rmdir_be() {
chflags -R noschg ${BE_MNTPT}
rm -rf ${BE_MNTPT}
}
unmount_be() {
mount | grep " on ${BE_MNTPT}" | awk '{print $3}' | sort -r | xargs -t umount -f
}
copy_pkgs() {
echo "Rsyncing back newly saved packages..."
rsync -av --progress ${BE_MNTPT}/var/cache/pkg/. /var/cache/pkg/.
}
cleanup_be() {
unmount_be
if [ -n "${created_be_dirs}" ]; then
chroot ${BE_MNTPT} /bin/rm -rf ${created_be_dirs}
fi
${BE_UTILITY} destroy -F ${BENAME}
}
create_be_dirs() {
echo "${BE_MNTPT}: Inspecting dirs $*"
for dir in $*; do
curdir="$dir"
topdir="$dir"
while :; do
[ -e "${BE_MNTPT}${curdir}" ] && break
topdir=$curdir
curdir=$(dirname ${curdir})
done
[ "$curdir" = "$dir" ] && continue
created_be_dirs="${topdir} ${created_be_dirs}"
echo "${BE_MNTPT}: Created ${dir}"
mkdir -p ${BE_MNTPT}${dir} || return $?
done
return 0
}
update_etcupdate_pre() {
${ETCUPDATE_CMD} -p -s ${srcdir} -D ${BE_MNTPT} ${ETCUPDATE_FLAGS} || return $?
${ETCUPDATE_CMD} resolve -D ${BE_MNTPT} || return $?
}
update_etcupdate() {
chroot ${BE_MNTPT} \
${ETCUPDATE_CMD} -s ${srcdir} ${ETCUPDATE_FLAGS} || return $?
chroot ${BE_MNTPT} ${ETCUPDATE_CMD} resolve
}
postmortem() {
[ -n "${BENAME}" ] || errx "Must specify BENAME"
[ -n "${BE_MNTPT}" ] || errx "Must specify BE_MNTPT"
echo "Performing post-mortem on BE ${BENAME} at ${BE_MNTPT} ..."
unmount_be
rmdir_be
echo "Post-mortem cleanup complete."
echo "To destroy the BE (recommended), run: ${BE_UTILITY} destroy ${BENAME}"
echo "To instead continue with the BE, run: ${BE_UTILITY} activate ${BENAME}"
}
if [ -n "$BEINSTALL_CMD" ]; then
${BEINSTALL_CMD} $*
exit $?
fi
if [ "$(basename -- "${BE_UTILITY}")" = "bectl" ]; then
${BE_UTILITY} check || errx "${BE_UTILITY} sanity check failed"
fi
cleanup_commands=""
trap 'errx "Interrupt caught"' HUP INT TERM
[ "$(whoami)" != "root" ] && errx "Must be run as root"
[ ! -f "Makefile.inc1" ] && errx "Must be in FreeBSD source tree"
srcdir=$(pwd)
objdir=$(make -V .OBJDIR 2>/dev/null)
[ ! -d "${objdir}" ] && errx "Must have built FreeBSD from source tree"
ETCUPDATE_CMD="${srcdir}/usr.sbin/etcupdate/etcupdate.sh"
if [ -e .git ] ; then
commit_time=$(git show -s --format='%ct' 2>/dev/null)
[ $? -ne 0 ] && errx "Can't lookup git commit timestamp"
commit_ts=$(date -r ${commit_time} '+%Y%m%d.%H%M%S')
elif [ -d .svn ] ; then
if [ -e /usr/bin/svnlite ]; then
svn=/usr/bin/svnlite
elif [ -e /usr/local/bin/svn ]; then
svn=/usr/local/bin/svn
else
errx "Unable to find subversion"
fi
commit_ts="$( "$svn" info --show-item last-changed-date | sed -e 's/\..*//' -e 's/T/./' -e 's/-//g' -e s'/://g' )"
[ $? -ne 0 ] && errx "Can't lookup Subversion commit timestamp"
else
errx "Unable to determine source control type"
fi
commit_ver=$(${objdir}/bin/freebsd-version/freebsd-version -u 2>/dev/null)
[ -z "${commit_ver}" ] && errx "Unable to determine FreeBSD version"
BENAME="${commit_ver}-${commit_ts}"
BE_TMP=$(mktemp -d /tmp/beinstall.XXXXXX)
[ $? -ne 0 -o ! -d ${BE_TMP} ] && errx "Unable to create mountpoint"
[ -z "$NO_CLEANUP_BE" ] && cleanup_commands="rmdir_be ${cleanup_commands}"
BE_MNTPT=${BE_TMP}/mnt
mkdir -p ${BE_MNTPT}
${BE_UTILITY} create ${BENAME} >/dev/null || errx "Unable to create BE ${BENAME}"
[ -z "$NO_CLEANUP_BE" ] && cleanup_commands="cleanup_be ${cleanup_commands}"
${BE_UTILITY} mount ${BENAME} ${BE_TMP}/mnt || errx "Unable to mount BE ${BENAME}."
echo "Mounted ${BENAME} to ${BE_MNTPT}, performing install/update ..."
make "$@" DESTDIR=${BE_MNTPT} installkernel || errx "Installkernel failed!"
if [ -n "${CONFIG_UPDATER}" ]; then
"update_${CONFIG_UPDATER}_pre"
[ $? -ne 0 ] && errx "${CONFIG_UPDATER} (pre-world) failed!"
fi
create_be_dirs "${srcdir}" "${objdir}" || errx "Unable to create BE dirs"
mount -t nullfs "${srcdir}" "${BE_MNTPT}${srcdir}" || errx "Unable to mount src"
mount -t nullfs "${objdir}" "${BE_MNTPT}${objdir}" || errx "Unable to mount obj"
mount -t devfs devfs "${BE_MNTPT}/dev" || errx "Unable to mount devfs"
chroot ${BE_MNTPT} make "$@" -C ${srcdir} installworld || \
errx "Installworld failed!"
if [ -n "${CONFIG_UPDATER}" ]; then
"update_${CONFIG_UPDATER}"
[ $? -ne 0 ] && errx "${CONFIG_UPDATER} (post-world) failed!"
fi
if which rsync >/dev/null 2>&1; then
cleanup_commands="copy_pkgs ${cleanup_commands}"
fi
BE_PKG="chroot ${BE_MNTPT} env ASSUME_ALWAYS_YES=true pkg"
if [ -z "${NO_PKG_UPGRADE}" ]; then
${BE_PKG} update || errx "Unable to update pkg"
${BE_PKG} upgrade || errx "Unable to upgrade pkgs"
fi
if [ -n "$NO_CLEANUP_BE" ]; then
echo "Boot Environment ${BENAME} may be examined in ${BE_MNTPT}."
echo "Afterwards, run this to cleanup:"
echo " env BENAME=${BENAME} BE_MNTPT=${BE_MNTPT} BEINSTALL_CMD=postmortem $0"
exit 0
fi
unmount_be || errx "Unable to unmount BE"
rmdir_be || errx "Unable to cleanup BE"
${BE_UTILITY} activate ${BENAME} || errx "Unable to activate BE"
echo
${BE_UTILITY} list
echo
echo "Boot environment ${BENAME} setup complete; reboot to use it."