]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/build/beinstall.sh
checkstyle9.pl: Add --github to output for github workflows
[FreeBSD/FreeBSD.git] / tools / build / beinstall.sh
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' and 'mergemaster' are supported.  Set to an
49 # empty string to skip.
50 CONFIG_UPDATER="${CONFIG_UPDATER:-"etcupdate"}"
51 # Flags for etcupdate if used.
52 ETCUPDATE_FLAGS="${ETCUPDATE_FLAGS:-"-F"}"
53 # Flags for mergemaster if used.
54 MERGEMASTER_FLAGS="${MERGEMASTER_FLAGS:-"-iFU"}"
55
56
57 ########################################################################
58 ## Functions
59 cleanup() {
60         [ -z "${cleanup_commands}" ] && return
61         echo "Cleaning up ..."
62         for command in ${cleanup_commands}; do
63                 ${command}
64         done
65 }
66
67 errx() {
68         cleanup
69         echo "error: $@"
70         exit 1
71 }
72
73 rmdir_be() {
74         chflags -R noschg ${BE_MNTPT}
75         rm -rf ${BE_MNTPT}
76 }
77
78 unmount_be() {
79         mount | grep " on ${BE_MNTPT}" | awk '{print $3}' | sort -r | xargs -t umount -f
80 }
81
82 copy_pkgs() {
83         # Before cleaning up, try to save progress in pkg(8) updates, to
84         # speed up future updates.  This is only called on the error path;
85         # no need to run on success.
86         echo "Rsyncing back newly saved packages..."
87         rsync -av --progress ${BE_MNTPT}/var/cache/pkg/. /var/cache/pkg/.
88 }
89
90 cleanup_be() {
91         # Before destroying, unmount any child filesystems that may have
92         # been mounted under the boot environment.  Sort them in reverse
93         # order so children are unmounted first.
94         unmount_be
95         # Finally, clean up any directories that were created by the
96         # operation, via cleanup_be_dirs().
97         if [ -n "${created_be_dirs}" ]; then
98                 chroot ${BE_MNTPT} /bin/rm -rf ${created_be_dirs}
99         fi
100         ${BE_UTILITY} destroy -F ${BENAME}
101 }
102
103 create_be_dirs() {
104         echo "${BE_MNTPT}: Inspecting dirs $*"
105         for dir in $*; do
106                 curdir="$dir"
107                 topdir="$dir"
108                 while :; do
109                         [ -e "${BE_MNTPT}${curdir}" ] && break
110                         topdir=$curdir
111                         curdir=$(dirname ${curdir})
112                 done
113                 [ "$curdir" = "$dir" ] && continue
114
115                 # Add the top-most nonexistent directory to the list, then
116                 # mkdir -p the innermost directory specified by the argument.
117                 # This way the least number of directories are rm'd directly.
118                 created_be_dirs="${topdir} ${created_be_dirs}"
119                 echo "${BE_MNTPT}: Created ${dir}"
120                 mkdir -p ${BE_MNTPT}${dir} || return $?
121         done
122         return 0
123 }
124
125 update_mergemaster_pre() {
126         ${MERGEMASTER_CMD} -p -m ${srcdir} -D ${BE_MNTPT} -t ${BE_MM_ROOT} ${MERGEMASTER_FLAGS}
127 }
128
129 update_mergemaster() {
130         ${MERGEMASTER_CMD} -m ${srcdir} -D ${BE_MNTPT} -t ${BE_MM_ROOT} ${MERGEMASTER_FLAGS}
131 }
132
133 update_etcupdate_pre() {
134         ${ETCUPDATE_CMD} -p -s ${srcdir} -D ${BE_MNTPT} ${ETCUPDATE_FLAGS} || return $?
135         ${ETCUPDATE_CMD} resolve -D ${BE_MNTPT} || return $?
136 }
137
138 update_etcupdate() {
139         chroot ${BE_MNTPT} \
140                 ${ETCUPDATE_CMD} -s ${srcdir} ${ETCUPDATE_FLAGS} || return $?
141         chroot ${BE_MNTPT} ${ETCUPDATE_CMD} resolve
142 }
143
144
145 # Special command-line subcommand that can be used to do a full cleanup
146 # after a manual post-mortem has been completed.
147 postmortem() {
148         [ -n "${BENAME}" ] || errx "Must specify BENAME"
149         [ -n "${BE_MNTPT}" ] || errx "Must specify BE_MNTPT"
150         echo "Performing post-mortem on BE ${BENAME} at ${BE_MNTPT} ..."
151         unmount_be
152         rmdir_be
153         echo "Post-mortem cleanup complete."
154         echo "To destroy the BE (recommended), run: ${BE_UTILITY} destroy ${BENAME}"
155         echo "To instead continue with the BE, run: ${BE_UTILITY} activate ${BENAME}"
156 }
157
158 if [ -n "$BEINSTALL_CMD" ]; then
159         ${BEINSTALL_CMD} $*
160         exit $?
161 fi
162
163 if [ "$(basename -- "${BE_UTILITY}")" = "bectl" ]; then
164         ${BE_UTILITY} check || errx "${BE_UTILITY} sanity check failed"
165 fi
166
167 cleanup_commands=""
168 trap 'errx "Interrupt caught"' HUP INT TERM
169
170 [ "$(whoami)" != "root" ] && errx "Must be run as root"
171
172 [ ! -f "Makefile.inc1" ] && errx "Must be in FreeBSD source tree"
173 srcdir=$(pwd)
174 objdir=$(make -V .OBJDIR 2>/dev/null)
175 [ ! -d "${objdir}" ] && errx "Must have built FreeBSD from source tree"
176
177 ## Constants
178 ETCUPDATE_CMD="${srcdir}/usr.sbin/etcupdate/etcupdate.sh"
179 MERGEMASTER_CMD="${srcdir}/usr.sbin/mergemaster/mergemaster.sh"
180
181 # May be a worktree, in which case .git is a file, not a directory.
182 if [ -e .git ] ; then
183     commit_time=$(git show -s --format='%ct' 2>/dev/null)
184     [ $? -ne 0 ] && errx "Can't lookup git commit timestamp"
185     commit_ts=$(date -r ${commit_time} '+%Y%m%d.%H%M%S')
186 elif [ -d .svn ] ; then
187       if [ -e /usr/bin/svnlite ]; then
188         svn=/usr/bin/svnlite
189       elif [ -e /usr/local/bin/svn ]; then
190         svn=/usr/local/bin/svn
191       else
192         errx "Unable to find subversion"
193       fi
194       commit_ts="$( "$svn" info --show-item last-changed-date | sed -e 's/\..*//' -e 's/T/./' -e 's/-//g' -e s'/://g' )"
195     [ $? -ne 0 ] && errx "Can't lookup Subversion commit timestamp"
196 else
197     errx "Unable to determine source control type"
198 fi
199
200 commit_ver=$(${objdir}/bin/freebsd-version/freebsd-version -u 2>/dev/null)
201 [ -z "${commit_ver}" ] && errx "Unable to determine FreeBSD version"
202
203 BENAME="${commit_ver}-${commit_ts}"
204
205 BE_TMP=$(mktemp -d /tmp/beinstall.XXXXXX)
206 [ $? -ne 0 -o ! -d ${BE_TMP} ] && errx "Unable to create mountpoint"
207 [ -z "$NO_CLEANUP_BE" ] && cleanup_commands="rmdir_be ${cleanup_commands}"
208 BE_MNTPT=${BE_TMP}/mnt
209 BE_MM_ROOT=${BE_TMP}/mergemaster # mergemaster will create
210 mkdir -p ${BE_MNTPT}
211
212 ${BE_UTILITY} create ${BENAME} >/dev/null || errx "Unable to create BE ${BENAME}"
213 [ -z "$NO_CLEANUP_BE" ] && cleanup_commands="cleanup_be ${cleanup_commands}"
214
215 ${BE_UTILITY} mount ${BENAME} ${BE_TMP}/mnt || errx "Unable to mount BE ${BENAME}."
216
217 echo "Mounted ${BENAME} to ${BE_MNTPT}, performing install/update ..."
218 make "$@" DESTDIR=${BE_MNTPT} installkernel || errx "Installkernel failed!"
219 if [ -n "${CONFIG_UPDATER}" ]; then
220         "update_${CONFIG_UPDATER}_pre"
221         [ $? -ne 0 ] && errx "${CONFIG_UPDATER} (pre-world) failed!"
222 fi
223
224 # Mount the source and object tree within the BE in order to account for any
225 # changes applied by the pre-installworld updater.  Cleanup any directories
226 # created if they didn't exist previously.
227 create_be_dirs "${srcdir}" "${objdir}" || errx "Unable to create BE dirs"
228 mount -t nullfs "${srcdir}" "${BE_MNTPT}${srcdir}" || errx "Unable to mount src"
229 mount -t nullfs "${objdir}" "${BE_MNTPT}${objdir}" || errx "Unable to mount obj"
230 mount -t devfs devfs "${BE_MNTPT}/dev" || errx "Unable to mount devfs"
231
232 chroot ${BE_MNTPT} make "$@" -C ${srcdir} installworld || \
233         errx "Installworld failed!"
234
235 if [ -n "${CONFIG_UPDATER}" ]; then
236         "update_${CONFIG_UPDATER}"
237         [ $? -ne 0 ] && errx "${CONFIG_UPDATER} (post-world) failed!"
238 fi
239
240 if which rsync >/dev/null 2>&1; then
241         cleanup_commands="copy_pkgs ${cleanup_commands}"
242 fi
243
244 BE_PKG="chroot ${BE_MNTPT} env ASSUME_ALWAYS_YES=true pkg"
245 if [ -z "${NO_PKG_UPGRADE}" ]; then
246         ${BE_PKG} update || errx "Unable to update pkg"
247         ${BE_PKG} upgrade || errx "Unable to upgrade pkgs"
248 fi
249
250 if [ -n "$NO_CLEANUP_BE" ]; then
251         echo "Boot Environment ${BENAME} may be examined in ${BE_MNTPT}."
252         echo "Afterwards, run this to cleanup:"
253         echo "  env BENAME=${BENAME} BE_MNTPT=${BE_MNTPT} BEINSTALL_CMD=postmortem $0"
254         exit 0
255 fi
256
257 unmount_be || errx "Unable to unmount BE"
258 rmdir_be || errx "Unable to cleanup BE"
259 ${BE_UTILITY} activate ${BENAME} || errx "Unable to activate BE"
260 echo
261 ${BE_UTILITY} list
262 echo
263 echo "Boot environment ${BENAME} setup complete; reboot to use it."