xref: /freebsd/tools/build/beinstall.sh (revision 18aef07cfac41fde763e1328366cc6e334bb9254)
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