xref: /openbsd-src/usr.sbin/sysupgrade/sysupgrade.sh (revision 36ac5f1008bb08354e7a0f92eb66bf572e4170bb)
1#!/bin/ksh
2#
3# $OpenBSD: sysupgrade.sh,v 1.57 2024/10/25 03:42:06 deraadt Exp $
4#
5# Copyright (c) 1997-2015 Todd Miller, Theo de Raadt, Ken Westerback
6# Copyright (c) 2015 Robert Peichaer <rpe@openbsd.org>
7# Copyright (c) 2016, 2017 Antoine Jacoutot <ajacoutot@openbsd.org>
8# Copyright (c) 2019 Christian Weisgerber <naddy@openbsd.org>
9# Copyright (c) 2019 Florian Obser <florian@openbsd.org>
10#
11# Permission to use, copy, modify, and distribute this software for any
12# purpose with or without fee is hereby granted, provided that the above
13# copyright notice and this permission notice appear in all copies.
14#
15# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23set -e
24umask 0022
25export PATH=/usr/bin:/bin:/usr/sbin:/sbin
26
27ARCH=$(uname -m)
28SETSDIR=/home/_sysupgrade
29
30err()
31{
32	echo "${0##*/}: ${1}" 1>&2
33	return ${2:-1}
34}
35
36usage()
37{
38	echo "usage: ${0##*/} [-fkns] [-b base-directory] [-R version] [installurl | path]" 1>&2
39	return 1
40}
41
42unpriv()
43{
44	local _file _rc=0 _user=_syspatch
45
46	if [[ $1 == -f ]]; then
47		_file=$2
48		shift 2
49	fi
50 	if [[ -n ${_file} ]]; then
51		>${_file}
52		chown "${_user}" "${_file}"
53	fi
54	(($# >= 1))
55
56	eval su -s /bin/sh ${_user} -c "'$@'" || _rc=$?
57
58	[[ -n ${_file} ]] && chown root "${_file}"
59
60	return ${_rc}
61}
62
63# Remove all occurrences of first argument from list formed by the remaining
64# arguments.
65rmel() {
66	local _a=$1 _b _c
67
68	shift
69	for _b; do
70		[[ $_a != "$_b" ]] && _c="${_c:+$_c }$_b"
71	done
72	echo -n "$_c"
73}
74
75SNAP=false
76FILE=false
77FORCE=false
78FORCE_VERSION=false
79KEEP=false
80REBOOT=true
81WHAT='release'
82
83VERSION=$(uname -r)
84NEXT_VERSION=$(echo ${VERSION} + 0.1 | bc)
85
86while getopts b:fknrR:s arg; do
87	case ${arg} in
88	b)	SETSDIR=${OPTARG}/_sysupgrade;;
89	f)	FORCE=true;;
90	k)	KEEP=true;;
91	n)	REBOOT=false;;
92	r)	;;
93	R)	FORCE_VERSION=true
94		[[ ${OPTARG} == @([0-9]|[0-9][0-9]).[0-9] ]] ||
95		    err "invalid version: ${OPTARG}"
96		NEXT_VERSION=${OPTARG};;
97	s)	SNAP=true;;
98	*)	usage;;
99	esac
100done
101
102(($(id -u) != 0)) && err "need root privileges"
103
104shift $(( OPTIND -1 ))
105
106case $# in
1070)	MIRROR=$(sed 's/#.*//;/^$/d' /etc/installurl) 2>/dev/null ||
108		MIRROR=https://cdn.openbsd.org/pub/OpenBSD
109	;;
1101)	MIRROR=$1
111	;;
112*)	usage
113esac
114[[ $MIRROR == @(file|ftp|http|https)://* ]] ||
115	FILE=true
116$FORCE_VERSION && $SNAP &&
117	err "incompatible options: -s -R $NEXT_VERSION"
118$FORCE && ! $SNAP &&
119	err "incompatible options: -f without -s"
120
121if $SNAP; then
122	WHAT='snapshot'
123	URL=${MIRROR}/snapshots/${ARCH}/
124else
125	URL=${MIRROR}/${NEXT_VERSION}/${ARCH}/
126	$FORCE_VERSION || ALT_URL=${MIRROR}/${VERSION}/${ARCH}/
127fi
128
129# Oh wait, this is a path install
130if $FILE; then
131	URL=file://$MIRROR/
132	ALT_URL=
133fi
134
135install -d -o 0 -g 0 -m 0755 ${SETSDIR}
136cd ${SETSDIR}
137
138echo "Fetching from ${URL}"
139if ! unpriv -f SHA256.sig ftp -N sysupgrade -Vmo SHA256.sig ${URL}SHA256.sig; then
140	if [[ -n ${ALT_URL} ]]; then
141		echo "Fetching from ${ALT_URL}"
142		unpriv -f SHA256.sig ftp -N sysupgrade -Vmo SHA256.sig ${ALT_URL}SHA256.sig
143		URL=${ALT_URL}
144		NEXT_VERSION=${VERSION}
145	else
146		exit 1
147	fi
148fi
149
150# The key extracted from SHA256.sig must precisely match a pattern
151KEY=$(head -1 < SHA256.sig | cut -d' ' -f5 | \
152	egrep '^openbsd-[[:digit:]]{2,3}-base.pub$' || true)
153if [[ -z $KEY ]]; then
154	echo "Invalid SHA256.sig file"
155	exit 1
156fi
157
158# If required key is not in the system, get it from a signed bundle
159if ! [[ -r /etc/signify/$KEY ]]; then
160	HAVEKEY=$(cd /etc/signify && ls -1 openbsd-*-base.pub | \
161	    tail -2 | head -1 | cut -d- -f2)
162	BUNDLE=sigbundle-${HAVEKEY}.tgz
163	FWKEY=$(echo $KEY | sed -e 's/base/fw/')
164	echo "Adding missing keys from bundle $BUNDLE"
165	unpriv -f ${BUNDLE} ftp -N sysupgrade -Vmo $BUNDLE https://ftp.openbsd.org/pub/OpenBSD/signify/$BUNDLE
166	signify -Vzq -m - -x $BUNDLE | (cd /etc/signify && tar xfz - $KEY $FWKEY)
167	rm $BUNDLE
168fi
169
170unpriv -f SHA256 signify -Ve -x SHA256.sig -m SHA256
171rm SHA256.sig
172
173if cmp -s /var/db/installed.SHA256 SHA256 && ! $FORCE; then
174	echo "Already on latest ${WHAT}."
175	exit 0
176fi
177
178unpriv -f BUILDINFO ftp -N sysupgrade -Vmo BUILDINFO ${URL}BUILDINFO
179unpriv cksum -qC SHA256 BUILDINFO
180
181if [[ -e /var/db/installed.BUILDINFO ]]; then
182	installed_build_ts=$(cut -f3 -d' ' /var/db/installed.BUILDINFO)
183	build_ts=$(cut -f3 -d' ' BUILDINFO)
184	if (( $build_ts <= $installed_build_ts )) && ! $FORCE; then
185		echo "Downloaded ${WHAT} is older than installed system. Use -f to force downgrade."
186		exit 1
187	fi
188fi
189
190# INSTALL.*, bsd*, *.tgz
191SETS=$(sed -n -e 's/^SHA256 (\(.*\)) .*/\1/' \
192    -e '/^INSTALL\./p;/^bsd/p;/\.tgz$/p' SHA256)
193
194OLD_FILES=$(ls)
195OLD_FILES=$(rmel SHA256 $OLD_FILES)
196DL=$SETS
197
198[[ -n ${OLD_FILES} ]] && echo Verifying old sets.
199for f in ${OLD_FILES}; do
200	if cksum -C SHA256 $f >/dev/null 2>&1; then
201		DL=$(rmel $f ${DL})
202		OLD_FILES=$(rmel $f ${OLD_FILES})
203	fi
204done
205
206[[ -n ${OLD_FILES} ]] && rm ${OLD_FILES}
207for f in ${DL}; do
208	unpriv -f $f ftp -N sysupgrade -Vmo ${f} ${URL}${f}
209done
210
211if [[ -n ${DL} ]]; then
212	echo Verifying sets.
213	unpriv cksum -qC SHA256 ${DL}
214fi
215
216cat <<__EOT >/auto_upgrade.conf
217Location of sets = disk
218Pathname to the sets = ${SETSDIR}/
219Directory does not contain SHA256.sig. Continue without verification = yes
220__EOT
221
222if ! ${KEEP}; then
223	CLEAN=$(echo SHA256 ${SETS} | sed -e 's/ /,/g')
224	cat <<__EOT > /etc/rc.firsttime
225rm -f ${SETSDIR}/{${CLEAN}}
226__EOT
227fi
228
229echo Fetching updated firmware.
230set -A _NEXTKERNV -- $(what bsd |
231	sed -n '2s/^[[:blank:]]OpenBSD \([1-9][0-9]*\.[0-9]\)\([^ ]*\).*/\1 \2/p')
232
233if [[ ${_NEXTKERNV[1]} == '-current' ]]; then
234	FW_URL=http://firmware.openbsd.org/firmware/snapshots/
235else
236	FW_URL=http://firmware.openbsd.org/firmware/${_NEXTKERNV[0]}/
237fi
238VNAME="${_NEXTKERNV[0]}" fw_update -p ${FW_URL} || true
239
240install -F -m 700 bsd.rd /bsd.upgrade
241logger -t sysupgrade -p kern.info "installed new /bsd.upgrade. Old kernel version: $(sysctl -n kern.version)"
242sync
243
244if ${REBOOT}; then
245	echo Upgrading.
246	exec reboot
247else
248	echo "Will upgrade on next reboot"
249fi
250