Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tools/build/beinstall.sh
39478 views
1
#!/bin/sh
2
#
3
# Copyright (c) 2016 Will Andrews
4
# All rights reserved.
5
#
6
# Redistribution and use in source and binary forms, with or without
7
# modification, are permitted provided that the following conditions
8
# are met:
9
# 1. Redistributions of source code must retain the above copyright
10
# notice, this list of conditions and the following disclaimer
11
# in this position and unchanged.
12
# 2. Redistributions in binary form must reproduce the above copyright
13
# notice, this list of conditions and the following disclaimer in the
14
# documentation and/or other materials provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
#
27
#
28
##
29
# Install a boot environment using the current FreeBSD source tree.
30
# Requires a fully built world & kernel.
31
#
32
# Non-base tools required: pkg
33
#
34
# In a sandbox for the new boot environment, this script also runs etcupdate
35
# and pkg upgrade automatically in the sandbox. Upon successful completion,
36
# the system will be ready to boot into the new boot environment. Upon
37
# failure, the target boot environment will be destroyed. In all cases, the
38
# running system is left untouched.
39
#
40
## Usage:
41
# beinstall [optional world/kernel flags e.g. KERNCONF]
42
#
43
## User modifiable variables - set these in the environment if desired.
44
# Utility to manage ZFS boot environments.
45
BE_UTILITY="${BE_UTILITY:-"bectl"}"
46
# If not empty, 'pkg upgrade' will be skipped.
47
NO_PKG_UPGRADE="${NO_PKG_UPGRADE:-""}"
48
# Config updater - 'etcupdate' is supported. Set to an empty string to skip.
49
CONFIG_UPDATER="${CONFIG_UPDATER:-"etcupdate"}"
50
# Flags for etcupdate if used.
51
ETCUPDATE_FLAGS="${ETCUPDATE_FLAGS:-"-BF"}"
52
53
54
########################################################################
55
## Functions
56
cleanup() {
57
[ -z "${cleanup_commands}" ] && return
58
echo "Cleaning up ..."
59
for command in ${cleanup_commands}; do
60
${command}
61
done
62
}
63
64
errx() {
65
cleanup
66
echo "error: $@"
67
exit 1
68
}
69
70
rmdir_be() {
71
chflags -R noschg ${BE_MNTPT}
72
rm -rf ${BE_MNTPT}
73
}
74
75
unmount_be() {
76
mount | grep " on ${BE_MNTPT}" | awk '{print $3}' | sort -r | xargs -t umount -f
77
}
78
79
copy_pkgs() {
80
# Before cleaning up, try to save progress in pkg(8) updates, to
81
# speed up future updates. This is only called on the error path;
82
# no need to run on success.
83
echo "Rsyncing back newly saved packages..."
84
rsync -av --progress ${BE_MNTPT}/var/cache/pkg/. /var/cache/pkg/.
85
}
86
87
cleanup_be() {
88
# Before destroying, unmount any child filesystems that may have
89
# been mounted under the boot environment. Sort them in reverse
90
# order so children are unmounted first.
91
unmount_be
92
# Finally, clean up any directories that were created by the
93
# operation, via cleanup_be_dirs().
94
if [ -n "${created_be_dirs}" ]; then
95
chroot ${BE_MNTPT} /bin/rm -rf ${created_be_dirs}
96
fi
97
${BE_UTILITY} destroy -F ${BENAME}
98
}
99
100
create_be_dirs() {
101
echo "${BE_MNTPT}: Inspecting dirs $*"
102
for dir in $*; do
103
curdir="$dir"
104
topdir="$dir"
105
while :; do
106
[ -e "${BE_MNTPT}${curdir}" ] && break
107
topdir=$curdir
108
curdir=$(dirname ${curdir})
109
done
110
[ "$curdir" = "$dir" ] && continue
111
112
# Add the top-most nonexistent directory to the list, then
113
# mkdir -p the innermost directory specified by the argument.
114
# This way the least number of directories are rm'd directly.
115
created_be_dirs="${topdir} ${created_be_dirs}"
116
echo "${BE_MNTPT}: Created ${dir}"
117
mkdir -p ${BE_MNTPT}${dir} || return $?
118
done
119
return 0
120
}
121
122
update_etcupdate_pre() {
123
${ETCUPDATE_CMD} -p -s ${srcdir} -D ${BE_MNTPT} ${ETCUPDATE_FLAGS} || return $?
124
${ETCUPDATE_CMD} resolve -D ${BE_MNTPT} || return $?
125
}
126
127
update_etcupdate() {
128
chroot ${BE_MNTPT} \
129
${ETCUPDATE_CMD} -s ${srcdir} ${ETCUPDATE_FLAGS} || return $?
130
chroot ${BE_MNTPT} ${ETCUPDATE_CMD} resolve
131
}
132
133
134
# Special command-line subcommand that can be used to do a full cleanup
135
# after a manual post-mortem has been completed.
136
postmortem() {
137
[ -n "${BENAME}" ] || errx "Must specify BENAME"
138
[ -n "${BE_MNTPT}" ] || errx "Must specify BE_MNTPT"
139
echo "Performing post-mortem on BE ${BENAME} at ${BE_MNTPT} ..."
140
unmount_be
141
rmdir_be
142
echo "Post-mortem cleanup complete."
143
echo "To destroy the BE (recommended), run: ${BE_UTILITY} destroy ${BENAME}"
144
echo "To instead continue with the BE, run: ${BE_UTILITY} activate ${BENAME}"
145
}
146
147
if [ -n "$BEINSTALL_CMD" ]; then
148
${BEINSTALL_CMD} $*
149
exit $?
150
fi
151
152
if [ "$(basename -- "${BE_UTILITY}")" = "bectl" ]; then
153
${BE_UTILITY} check || errx "${BE_UTILITY} sanity check failed"
154
fi
155
156
cleanup_commands=""
157
trap 'errx "Interrupt caught"' HUP INT TERM
158
159
[ "$(whoami)" != "root" ] && errx "Must be run as root"
160
161
[ ! -f "Makefile.inc1" ] && errx "Must be in FreeBSD source tree"
162
srcdir=$(pwd)
163
objdir=$(make -V .OBJDIR 2>/dev/null)
164
[ ! -d "${objdir}" ] && errx "Must have built FreeBSD from source tree"
165
166
## Constants
167
ETCUPDATE_CMD="${srcdir}/usr.sbin/etcupdate/etcupdate.sh"
168
169
# May be a worktree, in which case .git is a file, not a directory.
170
if [ -e .git ] ; then
171
commit_time=$(git show -s --format='%ct' 2>/dev/null)
172
[ $? -ne 0 ] && errx "Can't lookup git commit timestamp"
173
commit_ts=$(date -r ${commit_time} '+%Y%m%d.%H%M%S')
174
elif [ -d .svn ] ; then
175
if [ -e /usr/bin/svnlite ]; then
176
svn=/usr/bin/svnlite
177
elif [ -e /usr/local/bin/svn ]; then
178
svn=/usr/local/bin/svn
179
else
180
errx "Unable to find subversion"
181
fi
182
commit_ts="$( "$svn" info --show-item last-changed-date | sed -e 's/\..*//' -e 's/T/./' -e 's/-//g' -e s'/://g' )"
183
[ $? -ne 0 ] && errx "Can't lookup Subversion commit timestamp"
184
else
185
errx "Unable to determine source control type"
186
fi
187
188
commit_ver=$(${objdir}/bin/freebsd-version/freebsd-version -u 2>/dev/null)
189
[ -z "${commit_ver}" ] && errx "Unable to determine FreeBSD version"
190
191
BENAME="${commit_ver}-${commit_ts}"
192
193
BE_TMP=$(mktemp -d /tmp/beinstall.XXXXXX)
194
[ $? -ne 0 -o ! -d ${BE_TMP} ] && errx "Unable to create mountpoint"
195
[ -z "$NO_CLEANUP_BE" ] && cleanup_commands="rmdir_be ${cleanup_commands}"
196
BE_MNTPT=${BE_TMP}/mnt
197
mkdir -p ${BE_MNTPT}
198
199
${BE_UTILITY} create ${BENAME} >/dev/null || errx "Unable to create BE ${BENAME}"
200
[ -z "$NO_CLEANUP_BE" ] && cleanup_commands="cleanup_be ${cleanup_commands}"
201
202
${BE_UTILITY} mount ${BENAME} ${BE_TMP}/mnt || errx "Unable to mount BE ${BENAME}."
203
204
echo "Mounted ${BENAME} to ${BE_MNTPT}, performing install/update ..."
205
make "$@" DESTDIR=${BE_MNTPT} installkernel || errx "Installkernel failed!"
206
if [ -n "${CONFIG_UPDATER}" ]; then
207
"update_${CONFIG_UPDATER}_pre"
208
[ $? -ne 0 ] && errx "${CONFIG_UPDATER} (pre-world) failed!"
209
fi
210
211
# Mount the source and object tree within the BE in order to account for any
212
# changes applied by the pre-installworld updater. Cleanup any directories
213
# created if they didn't exist previously.
214
create_be_dirs "${srcdir}" "${objdir}" || errx "Unable to create BE dirs"
215
mount -t nullfs "${srcdir}" "${BE_MNTPT}${srcdir}" || errx "Unable to mount src"
216
mount -t nullfs "${objdir}" "${BE_MNTPT}${objdir}" || errx "Unable to mount obj"
217
mount -t devfs devfs "${BE_MNTPT}/dev" || errx "Unable to mount devfs"
218
219
chroot ${BE_MNTPT} make "$@" -C ${srcdir} installworld || \
220
errx "Installworld failed!"
221
222
if [ -n "${CONFIG_UPDATER}" ]; then
223
"update_${CONFIG_UPDATER}"
224
[ $? -ne 0 ] && errx "${CONFIG_UPDATER} (post-world) failed!"
225
fi
226
227
if which rsync >/dev/null 2>&1; then
228
cleanup_commands="copy_pkgs ${cleanup_commands}"
229
fi
230
231
BE_PKG="chroot ${BE_MNTPT} env ASSUME_ALWAYS_YES=true pkg"
232
if [ -z "${NO_PKG_UPGRADE}" ]; then
233
${BE_PKG} update || errx "Unable to update pkg"
234
${BE_PKG} upgrade || errx "Unable to upgrade pkgs"
235
fi
236
237
if [ -n "$NO_CLEANUP_BE" ]; then
238
echo "Boot Environment ${BENAME} may be examined in ${BE_MNTPT}."
239
echo "Afterwards, run this to cleanup:"
240
echo " env BENAME=${BENAME} BE_MNTPT=${BE_MNTPT} BEINSTALL_CMD=postmortem $0"
241
exit 0
242
fi
243
244
unmount_be || errx "Unable to unmount BE"
245
rmdir_be || errx "Unable to cleanup BE"
246
${BE_UTILITY} activate ${BENAME} || errx "Unable to activate BE"
247
echo
248
${BE_UTILITY} list
249
echo
250
echo "Boot environment ${BENAME} setup complete; reboot to use it."
251
252