File: [local] / openbsd / fw_update / fw_install.sh (download)
Revision 1.39, Wed Dec 1 02:19:00 2021 UTC (2 years, 7 months ago) by afresh1
Branch: MAIN
Changes since 1.38: +1 -1 lines
Assume the last matching firmware is the one we want
Because it's possible there will be more in the SHA256.sig file, but we will
put the onus of sorting to have the preferred one last to whoever creates that
file, since we are running in a very limited case.
|
#!/bin/ksh
# $OpenBSD$
set -e
# Copyright (c) 2021 Andrew Hewus Fresh <afresh1@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# Fake up some things from install.sub that we don't need to actually do
prefetcharea_fs_list() {
echo "${DESTDIR}/tmp"
}
# tmpdir, do_as, unpriv, and unpriv2 are from install.sub
# Create a temporary directory based on the supplied directory name prefix.
tmpdir() {
local _i=1 _dir
until _dir="${1?}.$_i.$RANDOM" && mkdir -- "$_dir" 2>/dev/null; do
((++_i < 10000)) || return 1
done
echo "$_dir"
}
# Run a command ($2+) as unprivileged user ($1).
# Take extra care that after "cmd" no "user" processes exist.
#
# Optionally:
# - create "file" and chown it to "user"
# - after "cmd", chown "file" back to root
#
# Usage: do_as user [-f file] cmd
do_as() {
(( $# >= 2 )) || return
local _file _rc _user=$1
shift
if [[ $1 == -f ]]; then
_file=$2
shift 2
fi
if [[ -n $_file ]]; then
>$_file
chown "$_user" "$_file"
fi
doas -u "$_user" "$@"
_rc=$?
while doas -u "$_user" kill -9 -1 2>/dev/null; do
echo "Processes still running for user $_user after: $@"
sleep 1
done
[[ -n $_file ]] && chown root "$_file"
return $_rc
}
unpriv() {
do_as _sndio "$@"
}
unpriv2() {
do_as _file "$@"
}
VNAME=${VNAME:-$(sysctl -n kern.osrelease)}
VERSION=${VERSION:-"${VNAME%.*}${VNAME#*.}"}
FWDIR=${FWDIR:-$VNAME}
MODE=${MODE:-install}
# TODO: We need the firmware for the system we just installed
# not the one we booted from. For example:
# * booting from a snapshot bsd.rd that thinks it is the 7.0 release
# will install the firmware from the 7.0 directory instead of
# from the snapshots dir.
# If they're using sysupgrade, then the installer kernel will be correct.
# If we're doing this in the installer we can check what they picked
# for downloading sets and use that value.
# Otherwise, the fw_update after first boot will fix it up for us.
HTTP_FWDIR=$FWDIR
set -- sed -n "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p" /var/run/dmesg.boot
[[ $1 == -!(stable) ]] && HTTP_FWDIR=snapshots
FWURL=http://firmware.openbsd.org/firmware/${HTTP_FWDIR}
FWPUB_KEY=${DESTDIR}/etc/signify/openbsd-${VERSION}-fw.pub
FWPATTERNS="${DESTDIR}/usr/share/misc/firmware_patterns"
# TODO: support srclocal installation of firmware somehow
fw_install() {
local _src=$1 _tmpfs_list _tmpfs _tmpsrc \
_t=Get _cfile="/tmp/SHA256" _pkgdir=${DESTDIR}/var/db/pkg \
_f _r _remove _i _installed
local _srclocal=false _unpriv=unpriv
echo "Let's $MODE firmware!"
local _d _drivers=$(
last=''
while read _d _m; do
grep=grep
[ "$last" = "$_d" ] && continue
[ "$_m" ] || _m="^$_d[0-9][0-9]* at "
[ "$_m" = "${_m#^}" ] && grep=fgrep
$grep -q "$_m" /var/run/dmesg.boot || continue
echo $_d
last=$_d
done < $FWPATTERNS
)
if [ -z "$_drivers" ]; then
echo "No devices found which need firmware files to be downloaded."
return
fi
! _tmpfs_list=$(prefetcharea_fs_list) &&
echo "Cannot determine prefetch area" >&2 && return
for _tmpfs in $_tmpfs_list; do
# Try to clean up from previous runs, assuming
# the _tmpfs selection yields the same mount
# point.
for _tmpsrc in $_tmpfs/firmware.+([0-9]).+([0-9]); do
[[ -d $_tmpsrc ]] && rm -r $_tmpsrc
done
# Create a download directory for the firmware and
# check that the _sndio user can read files from
# it. Otherwise cleanup and skip the filesystem.
if _tmpsrc=$(tmpdir "$_tmpfs/firmware"); then
(
>$_tmpsrc/t &&
$_unpriv cat $_tmpsrc/t
) >/dev/null 2>&1 && break ||
rm -r $_tmpsrc
fi
done
[[ ! -d $_tmpsrc ]] &&
echo "Cannot create prefetch area" >&2 && return 1
# Cleanup from previous runs.
rm -f $_cfile $_cfile.sig
_t=Get/Verify
! $_unpriv ftp -D "$_t" -Vmo - "$_src/SHA256.sig" >"$_cfile.sig" &&
echo "Cannot fetch SHA256.sig" >&2 && return 1
# Verify signature file with public keys.
! $_unpriv -f "$_cfile" \
signify -Vep $FWPUB_KEY -x "$_cfile.sig" -m "$_cfile" &&
echo "Signature check of SHA256.sig failed" >&2 && return 1
for _d in $_drivers; do
_f=$( sed -n "s/.*(\($_d-firmware-.*\.tgz\)).*/\1/p" "$_cfile" | tail -1 )
_installed=$(
for fw in "${_pkgdir}/$_d-firmware"*; do
[ -e "$fw" ] || continue
echo ${fw##*/}
done
)
for _i in $_installed; do
if [ "$_f" = "$_i.tgz" ]; then
echo "$_i already installed"
continue 2
fi
done
rm -f /tmp/h /tmp/fail
# Fetch firmware file and create a checksum by piping through
# sha256. Create a flag file in case ftp failed. Firmware
# from net is written to the prefetch area.
( $_unpriv ftp -D "$_t" -Vmo - "$_src/$_f" || >/tmp/fail ) |
( $_srclocal && unpriv2 sha256 -b >/tmp/h ||
unpriv2 -f /tmp/h sha256 -bph /tmp/h >"$_tmpsrc/$_f" )
# Handle failed transfer.
if [[ -f /tmp/fail ]]; then
rm -f "$_tmpsrc/$_f"
echo "Fetching of $_f failed!" >&2
continue
fi
# Verify firmware by comparing its checksum with SHA256.
if ! fgrep -qx "SHA256 ($_f) = $(</tmp/h)" "$_cfile"; then
[[ -d "$_tmpsrc" ]] && rm -rf "$_tmpsrc"
echo "Checksum test for $_f failed." >&2
continue
fi
# TODO: Check hash for files before deleting
if [ "$_installed" ] && [ -e "${_pkgdir}/$_installed/+CONTENTS" ]; then
echo "Uninstalling $_installed"
cwd=${_pkgdir}/$_installed
set -A _remove -- "${cwd}/+CONTENTS" "${cwd}"
while read c g; do
case $c in
@cwd) cwd="${DESTDIR}/$g"
;;
@*) continue
;;
*) set -A _remove -- "$cwd/$c" "${_remove[@]}"
;;
esac
done < "${_pkgdir}/$_installed/+CONTENTS"
# We specifically rm -f here because not removing files/dirs
# is probably not worth failing over.
for _r in "${_remove[@]}" ; do
if [ -d "$_r" ]; then
# Try hard not to actually remove recursively
# without rmdir on the install media.
[ "$_r/*" = $( echo "$_r"/* ) ] && rm -rf "$_r"
else
rm -f "$_r"
fi
done
fi
# TODO: Should we mark these so real fw_update can -Drepair?
ftp -D "Install" -Vmo- "file:$_tmpsrc/$_f" |
tar -s ",^\+,${_pkgdir}/${_f%.tgz}/+," \
-s ",^firmware,${DESTDIR}/etc/firmware," \
-C / -zxphf - "+*" "firmware/*"
ed -s "${_pkgdir}/${_f%.tgz}/+CONTENTS" <<EOL
/^@comment pkgpath/ -1a
@option manual-installation
@option firmware
@comment install-script
.
w
EOL
done
}
fw_install "$FWURL"