1462819bbSWill Andrews#!/bin/sh 2462819bbSWill Andrews# 3462819bbSWill Andrews# Copyright (c) 2016 Will Andrews 4462819bbSWill Andrews# All rights reserved. 5462819bbSWill Andrews# 6462819bbSWill Andrews# Redistribution and use in source and binary forms, with or without 7462819bbSWill Andrews# modification, are permitted provided that the following conditions 8462819bbSWill Andrews# are met: 9462819bbSWill Andrews# 1. Redistributions of source code must retain the above copyright 10462819bbSWill Andrews# notice, this list of conditions and the following disclaimer 11462819bbSWill Andrews# in this position and unchanged. 12462819bbSWill Andrews# 2. Redistributions in binary form must reproduce the above copyright 13462819bbSWill Andrews# notice, this list of conditions and the following disclaimer in the 14462819bbSWill Andrews# documentation and/or other materials provided with the distribution. 15462819bbSWill Andrews# 16462819bbSWill Andrews# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17462819bbSWill Andrews# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18462819bbSWill Andrews# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19462819bbSWill Andrews# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20462819bbSWill Andrews# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21462819bbSWill Andrews# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22462819bbSWill Andrews# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23462819bbSWill Andrews# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24462819bbSWill Andrews# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25462819bbSWill Andrews# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26462819bbSWill Andrews# 27462819bbSWill Andrews# 28462819bbSWill Andrews## 29462819bbSWill Andrews# Install a boot environment using the current FreeBSD source tree. 30462819bbSWill Andrews# Requires a fully built world & kernel. 31462819bbSWill Andrews# 32eb7f380fSMateusz Piotrowski# Non-base tools required: pkg 33462819bbSWill Andrews# 34462819bbSWill Andrews# In a sandbox for the new boot environment, this script also runs etcupdate 35462819bbSWill Andrews# and pkg upgrade automatically in the sandbox. Upon successful completion, 36462819bbSWill Andrews# the system will be ready to boot into the new boot environment. Upon 37462819bbSWill Andrews# failure, the target boot environment will be destroyed. In all cases, the 38462819bbSWill Andrews# running system is left untouched. 39462819bbSWill Andrews# 40462819bbSWill Andrews## Usage: 41462819bbSWill Andrews# beinstall [optional world/kernel flags e.g. KERNCONF] 42462819bbSWill Andrews# 43462819bbSWill Andrews## User modifiable variables - set these in the environment if desired. 44eb7f380fSMateusz Piotrowski# Utility to manage ZFS boot environments. 45eb7f380fSMateusz PiotrowskiBE_UTILITY="${BE_UTILITY:-"bectl"}" 46462819bbSWill Andrews# If not empty, 'pkg upgrade' will be skipped. 47462819bbSWill AndrewsNO_PKG_UPGRADE="${NO_PKG_UPGRADE:-""}" 48b07689d1SEd Maste# Config updater - 'etcupdate' is supported. Set to an empty string to skip. 49462819bbSWill AndrewsCONFIG_UPDATER="${CONFIG_UPDATER:-"etcupdate"}" 50462819bbSWill Andrews# Flags for etcupdate if used. 51*18aef07cSJessica ClarkeETCUPDATE_FLAGS="${ETCUPDATE_FLAGS:-"-BF"}" 52462819bbSWill Andrews 53462819bbSWill Andrews 54462819bbSWill Andrews######################################################################## 55462819bbSWill Andrews## Functions 56462819bbSWill Andrewscleanup() { 57462819bbSWill Andrews [ -z "${cleanup_commands}" ] && return 58462819bbSWill Andrews echo "Cleaning up ..." 59462819bbSWill Andrews for command in ${cleanup_commands}; do 60462819bbSWill Andrews ${command} 61462819bbSWill Andrews done 62462819bbSWill Andrews} 63462819bbSWill Andrews 64462819bbSWill Andrewserrx() { 65462819bbSWill Andrews cleanup 66fc3bfe91SEitan Adler echo "error: $@" 67462819bbSWill Andrews exit 1 68462819bbSWill Andrews} 69462819bbSWill Andrews 70462819bbSWill Andrewsrmdir_be() { 71462819bbSWill Andrews chflags -R noschg ${BE_MNTPT} 72462819bbSWill Andrews rm -rf ${BE_MNTPT} 73462819bbSWill Andrews} 74462819bbSWill Andrews 7516702050SWill Andrewsunmount_be() { 7616702050SWill Andrews mount | grep " on ${BE_MNTPT}" | awk '{print $3}' | sort -r | xargs -t umount -f 7716702050SWill Andrews} 7816702050SWill Andrews 792728d630SWill Andrewscopy_pkgs() { 802728d630SWill Andrews # Before cleaning up, try to save progress in pkg(8) updates, to 812728d630SWill Andrews # speed up future updates. This is only called on the error path; 822728d630SWill Andrews # no need to run on success. 832728d630SWill Andrews echo "Rsyncing back newly saved packages..." 842728d630SWill Andrews rsync -av --progress ${BE_MNTPT}/var/cache/pkg/. /var/cache/pkg/. 852728d630SWill Andrews} 862728d630SWill Andrews 87462819bbSWill Andrewscleanup_be() { 8816702050SWill Andrews # Before destroying, unmount any child filesystems that may have 8916702050SWill Andrews # been mounted under the boot environment. Sort them in reverse 9016702050SWill Andrews # order so children are unmounted first. 9116702050SWill Andrews unmount_be 9216702050SWill Andrews # Finally, clean up any directories that were created by the 9316702050SWill Andrews # operation, via cleanup_be_dirs(). 9416702050SWill Andrews if [ -n "${created_be_dirs}" ]; then 9516702050SWill Andrews chroot ${BE_MNTPT} /bin/rm -rf ${created_be_dirs} 9616702050SWill Andrews fi 97eb7f380fSMateusz Piotrowski ${BE_UTILITY} destroy -F ${BENAME} 98462819bbSWill Andrews} 99462819bbSWill Andrews 10016702050SWill Andrewscreate_be_dirs() { 10116702050SWill Andrews echo "${BE_MNTPT}: Inspecting dirs $*" 10216702050SWill Andrews for dir in $*; do 10316702050SWill Andrews curdir="$dir" 10416702050SWill Andrews topdir="$dir" 10516702050SWill Andrews while :; do 10616702050SWill Andrews [ -e "${BE_MNTPT}${curdir}" ] && break 10716702050SWill Andrews topdir=$curdir 10816702050SWill Andrews curdir=$(dirname ${curdir}) 10916702050SWill Andrews done 11016702050SWill Andrews [ "$curdir" = "$dir" ] && continue 11116702050SWill Andrews 11216702050SWill Andrews # Add the top-most nonexistent directory to the list, then 11316702050SWill Andrews # mkdir -p the innermost directory specified by the argument. 11416702050SWill Andrews # This way the least number of directories are rm'd directly. 11516702050SWill Andrews created_be_dirs="${topdir} ${created_be_dirs}" 11616702050SWill Andrews echo "${BE_MNTPT}: Created ${dir}" 11716702050SWill Andrews mkdir -p ${BE_MNTPT}${dir} || return $? 11816702050SWill Andrews done 11916702050SWill Andrews return 0 12016702050SWill Andrews} 12116702050SWill Andrews 12216702050SWill Andrewsupdate_etcupdate_pre() { 1231935b8f9SBrad Davis ${ETCUPDATE_CMD} -p -s ${srcdir} -D ${BE_MNTPT} ${ETCUPDATE_FLAGS} || return $? 1241935b8f9SBrad Davis ${ETCUPDATE_CMD} resolve -D ${BE_MNTPT} || return $? 125462819bbSWill Andrews} 126462819bbSWill Andrews 127462819bbSWill Andrewsupdate_etcupdate() { 12816702050SWill Andrews chroot ${BE_MNTPT} \ 1291935b8f9SBrad Davis ${ETCUPDATE_CMD} -s ${srcdir} ${ETCUPDATE_FLAGS} || return $? 1301935b8f9SBrad Davis chroot ${BE_MNTPT} ${ETCUPDATE_CMD} resolve 131462819bbSWill Andrews} 132462819bbSWill Andrews 133462819bbSWill Andrews 13416702050SWill Andrews# Special command-line subcommand that can be used to do a full cleanup 13516702050SWill Andrews# after a manual post-mortem has been completed. 13616702050SWill Andrewspostmortem() { 13716702050SWill Andrews [ -n "${BENAME}" ] || errx "Must specify BENAME" 13816702050SWill Andrews [ -n "${BE_MNTPT}" ] || errx "Must specify BE_MNTPT" 13916702050SWill Andrews echo "Performing post-mortem on BE ${BENAME} at ${BE_MNTPT} ..." 14016702050SWill Andrews unmount_be 14116702050SWill Andrews rmdir_be 14216702050SWill Andrews echo "Post-mortem cleanup complete." 143eb7f380fSMateusz Piotrowski echo "To destroy the BE (recommended), run: ${BE_UTILITY} destroy ${BENAME}" 144eb7f380fSMateusz Piotrowski echo "To instead continue with the BE, run: ${BE_UTILITY} activate ${BENAME}" 14516702050SWill Andrews} 14616702050SWill Andrews 14716702050SWill Andrewsif [ -n "$BEINSTALL_CMD" ]; then 14816702050SWill Andrews ${BEINSTALL_CMD} $* 14916702050SWill Andrews exit $? 15016702050SWill Andrewsfi 15116702050SWill Andrews 152eb7f380fSMateusz Piotrowskiif [ "$(basename -- "${BE_UTILITY}")" = "bectl" ]; then 153eb7f380fSMateusz Piotrowski ${BE_UTILITY} check || errx "${BE_UTILITY} sanity check failed" 154eb7f380fSMateusz Piotrowskifi 15516702050SWill Andrews 156462819bbSWill Andrewscleanup_commands="" 157462819bbSWill Andrewstrap 'errx "Interrupt caught"' HUP INT TERM 158462819bbSWill Andrews 159462819bbSWill Andrews[ "$(whoami)" != "root" ] && errx "Must be run as root" 160462819bbSWill Andrews 161462819bbSWill Andrews[ ! -f "Makefile.inc1" ] && errx "Must be in FreeBSD source tree" 16216702050SWill Andrewssrcdir=$(pwd) 163462819bbSWill Andrewsobjdir=$(make -V .OBJDIR 2>/dev/null) 164462819bbSWill Andrews[ ! -d "${objdir}" ] && errx "Must have built FreeBSD from source tree" 165462819bbSWill Andrews 1661935b8f9SBrad Davis## Constants 1671935b8f9SBrad DavisETCUPDATE_CMD="${srcdir}/usr.sbin/etcupdate/etcupdate.sh" 1681935b8f9SBrad Davis 1697d4a3185SWill Andrews# May be a worktree, in which case .git is a file, not a directory. 1707d4a3185SWill Andrewsif [ -e .git ] ; then 171a0c50edaSJoseph Mingrone commit_time=$(git show -s --format='%ct' 2>/dev/null) 172462819bbSWill Andrews [ $? -ne 0 ] && errx "Can't lookup git commit timestamp" 173462819bbSWill Andrews commit_ts=$(date -r ${commit_time} '+%Y%m%d.%H%M%S') 174462819bbSWill Andrewselif [ -d .svn ] ; then 175fc3bfe91SEitan Adler if [ -e /usr/bin/svnlite ]; then 176fc3bfe91SEitan Adler svn=/usr/bin/svnlite 177fc3bfe91SEitan Adler elif [ -e /usr/local/bin/svn ]; then 178fc3bfe91SEitan Adler svn=/usr/local/bin/svn 1791ff30c6aSBrad Davis else 180fc3bfe91SEitan Adler errx "Unable to find subversion" 1811ff30c6aSBrad Davis fi 182fc3bfe91SEitan Adler commit_ts="$( "$svn" info --show-item last-changed-date | sed -e 's/\..*//' -e 's/T/./' -e 's/-//g' -e s'/://g' )" 183462819bbSWill Andrews [ $? -ne 0 ] && errx "Can't lookup Subversion commit timestamp" 184462819bbSWill Andrewselse 185fc3bfe91SEitan Adler errx "Unable to determine source control type" 186462819bbSWill Andrewsfi 187462819bbSWill Andrews 188462819bbSWill Andrewscommit_ver=$(${objdir}/bin/freebsd-version/freebsd-version -u 2>/dev/null) 189462819bbSWill Andrews[ -z "${commit_ver}" ] && errx "Unable to determine FreeBSD version" 190462819bbSWill Andrews 191462819bbSWill AndrewsBENAME="${commit_ver}-${commit_ts}" 192462819bbSWill Andrews 193462819bbSWill AndrewsBE_TMP=$(mktemp -d /tmp/beinstall.XXXXXX) 194462819bbSWill Andrews[ $? -ne 0 -o ! -d ${BE_TMP} ] && errx "Unable to create mountpoint" 195462819bbSWill Andrews[ -z "$NO_CLEANUP_BE" ] && cleanup_commands="rmdir_be ${cleanup_commands}" 196462819bbSWill AndrewsBE_MNTPT=${BE_TMP}/mnt 197462819bbSWill Andrewsmkdir -p ${BE_MNTPT} 198462819bbSWill Andrews 199eb7f380fSMateusz Piotrowski${BE_UTILITY} create ${BENAME} >/dev/null || errx "Unable to create BE ${BENAME}" 200462819bbSWill Andrews[ -z "$NO_CLEANUP_BE" ] && cleanup_commands="cleanup_be ${cleanup_commands}" 201462819bbSWill Andrews 202eb7f380fSMateusz Piotrowski${BE_UTILITY} mount ${BENAME} ${BE_TMP}/mnt || errx "Unable to mount BE ${BENAME}." 203462819bbSWill Andrews 204462819bbSWill Andrewsecho "Mounted ${BENAME} to ${BE_MNTPT}, performing install/update ..." 205fc3bfe91SEitan Adlermake "$@" DESTDIR=${BE_MNTPT} installkernel || errx "Installkernel failed!" 20616702050SWill Andrewsif [ -n "${CONFIG_UPDATER}" ]; then 20716702050SWill Andrews "update_${CONFIG_UPDATER}_pre" 20816702050SWill Andrews [ $? -ne 0 ] && errx "${CONFIG_UPDATER} (pre-world) failed!" 20916702050SWill Andrewsfi 21016702050SWill Andrews 21116702050SWill Andrews# Mount the source and object tree within the BE in order to account for any 21216702050SWill Andrews# changes applied by the pre-installworld updater. Cleanup any directories 21316702050SWill Andrews# created if they didn't exist previously. 21416702050SWill Andrewscreate_be_dirs "${srcdir}" "${objdir}" || errx "Unable to create BE dirs" 21516702050SWill Andrewsmount -t nullfs "${srcdir}" "${BE_MNTPT}${srcdir}" || errx "Unable to mount src" 21616702050SWill Andrewsmount -t nullfs "${objdir}" "${BE_MNTPT}${objdir}" || errx "Unable to mount obj" 2171655b231SMateusz Piotrowskimount -t devfs devfs "${BE_MNTPT}/dev" || errx "Unable to mount devfs" 21816702050SWill Andrews 21916702050SWill Andrewschroot ${BE_MNTPT} make "$@" -C ${srcdir} installworld || \ 22016702050SWill Andrews errx "Installworld failed!" 221462819bbSWill Andrews 222462819bbSWill Andrewsif [ -n "${CONFIG_UPDATER}" ]; then 223462819bbSWill Andrews "update_${CONFIG_UPDATER}" 22416702050SWill Andrews [ $? -ne 0 ] && errx "${CONFIG_UPDATER} (post-world) failed!" 225462819bbSWill Andrewsfi 226462819bbSWill Andrews 2272728d630SWill Andrewsif which rsync >/dev/null 2>&1; then 2282728d630SWill Andrews cleanup_commands="copy_pkgs ${cleanup_commands}" 2292728d630SWill Andrewsfi 2302728d630SWill Andrews 231462819bbSWill AndrewsBE_PKG="chroot ${BE_MNTPT} env ASSUME_ALWAYS_YES=true pkg" 232462819bbSWill Andrewsif [ -z "${NO_PKG_UPGRADE}" ]; then 233462819bbSWill Andrews ${BE_PKG} update || errx "Unable to update pkg" 234462819bbSWill Andrews ${BE_PKG} upgrade || errx "Unable to upgrade pkgs" 235462819bbSWill Andrewsfi 236462819bbSWill Andrews 23716702050SWill Andrewsif [ -n "$NO_CLEANUP_BE" ]; then 23816702050SWill Andrews echo "Boot Environment ${BENAME} may be examined in ${BE_MNTPT}." 23916702050SWill Andrews echo "Afterwards, run this to cleanup:" 24016702050SWill Andrews echo " env BENAME=${BENAME} BE_MNTPT=${BE_MNTPT} BEINSTALL_CMD=postmortem $0" 24116702050SWill Andrews exit 0 24216702050SWill Andrewsfi 24316702050SWill Andrews 24416702050SWill Andrewsunmount_be || errx "Unable to unmount BE" 24516702050SWill Andrewsrmdir_be || errx "Unable to cleanup BE" 246eb7f380fSMateusz Piotrowski${BE_UTILITY} activate ${BENAME} || errx "Unable to activate BE" 247462819bbSWill Andrewsecho 248eb7f380fSMateusz Piotrowski${BE_UTILITY} list 249462819bbSWill Andrewsecho 250462819bbSWill Andrewsecho "Boot environment ${BENAME} setup complete; reboot to use it." 251