File: [local] / openbsd / fw_update / fw_install.sh (download)
Revision 1.85, Sat Dec 18 22:03:43 2021 UTC (2 years, 6 months ago) by afresh1
Branch: MAIN
Changes since 1.84: +35 -5 lines
Wrap ftp in a timer
That is, we put ftp in the background and check every second that
it's still running and every 12 seconds that the filesize has
changed. If it hasn't, we interrupt the download and abort.
|
#!/bin/ksh
# $OpenBSD$
#
# 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.
set -o errexit -o pipefail -o nounset
set +o monitor
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
CFILE=SHA256.sig
DESTDIR=${DESTDIR:-}
FWPATTERNS="${DESTDIR}/usr/share/misc/firmware_patterns"
VNAME=${VNAME:-$(sysctl -n kern.osrelease)}
VERSION=${VERSION:-"${VNAME%.*}${VNAME#*.}"}
HTTP_FWDIR="$VNAME"
VTYPE=$( sed -n "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p" \
/var/run/dmesg.boot | sed '$!d' )
[[ $VTYPE == -!(stable) ]] && HTTP_FWDIR=snapshots
FWURL=http://firmware.openbsd.org/firmware/${HTTP_FWDIR}
FWPUB_KEY=${DESTDIR}/etc/signify/openbsd-${VERSION}-fw.pub
LOCALSRC=
tmpdir() {
local _i=1 _dir
# If we're not in the installer,
# we have mktemp and a more hostile environment.
if [ -x /usr/bin/mktemp ]; then
_dir=$( mktemp -d "${1}-XXXXXXXXX" )
else
until _dir="${1}.$_i.$RANDOM" && mkdir -- "$_dir" 2>/dev/null; do
((++_i < 10000)) || return 1
done
fi
echo "$_dir"
}
fetch() {
local _src="${FWURL}/${1##*/}" _dst=$1 _user=_file _pid _exit
# If we're not in the installer,
# we have su(1) and doas(1) is unlikely to be configured.
set -o monitor # make sure ftp gets its own process group
(
if [ -x /usr/bin/su ]; then
exec /usr/bin/su -s /bin/ksh "$_user" -c \
"/usr/bin/ftp -D 'Get/Verify' -Vm -o- '$_src'" > "$_dst"
else
exec /usr/bin/doas -u "$_user" \
/usr/bin/ftp -D 'Get/Verify' -Vm -o- "$_src" > "$_dst"
fi
) & _pid=$!
set +o monitor
trap "kill -TERM '-$_pid'; exit 1" EXIT INT QUIT ABRT TERM
SECONDS=0
_last=0
while kill -0 -"$_pid" 2>/dev/null; do
if [[ $SECONDS -gt 12 ]]; then
set -- $( ls -ln "$_dst" 2>/dev/null )
if [[ $_last -ne $5 ]]; then
_last=$5
SECONDS=0
sleep 1
else
kill -INT -"$_pid"
echo "fetch timed out" >&2
fi
else
sleep 1
fi
done
set +o errexit
wait "$_pid"
_exit=$?
set -o errexit
trap "" EXIT INT QUIT ABRT TERM
if [ "$_exit" -ne 0 ]; then
rm -f "$_dst"
echo "Cannot fetch $_src" >&2
return 1
fi
}
verify() {
# On the installer we don't get sha256 -C, so fake it.
if ! fgrep -qx "SHA256 (${1##*/}) = $( /bin/sha256 -qb "$1" )" "$CFILE"; then
echo "Checksum test for ${1##*/} failed." >&2
return 1
fi
}
devices_needing_firmware() {
local _d _m _line _dmesgtail _last=''
# When we're not in the installer, the dmesg.boot can
# contain multiple boots, so only look in the last one
sed -n 'H;/^OpenBSD/h;${g;p;}' /var/run/dmesg.boot |
grep -e "^[a-z][a-z]*[0-9]" -e " not configured " | { \
_m=0
set -A _dmesgtail
while read -r _line; do
_dmesgtail[$_m]="$_line"
let _m=$_m+1
done
grep -v '^[[:space:]]*#' "$FWPATTERNS" |
while read -r _d _m; do
[ "$_d" = "$_last" ] && continue
[ "$_m" ] || _m="^${_d}[0-9] at "
if [ "$_m" = "${_m#^}" ]; then
_m="*$_m"
else
_m="${_m#^}"
fi
for _line in "${_dmesgtail[@]}"; do
if [[ $_line = ${_m}* ]]; then
echo "$_d"
_last="$_d"
fi
done
done
}
}
firmware_filename() {
local _f
_f="$( sed -n "s/.*(\($1-firmware-.*\.tgz\)).*/\1/p" "$CFILE" | sed '$!d' )"
! [ "$_f" ] && echo "Unable to find firmware for $1" >&2 && return 1
echo "$_f"
}
firmware_devicename() {
local _d="${1##*/}"
_d="${_d%-firmware-*}"
echo "$_d"
}
installed_firmware() {
for fw in "${DESTDIR}/var/db/pkg/$1-firmware"*; do
[ -e "$fw" ] || continue
echo "${fw##*/}"
done
}
add_firmware () {
local _f="${1##*/}"
local _pkgdir="${DESTDIR}/var/db/pkg/${_f%.tgz}"
ftp -D "Install" -Vmo- "file:${1}" |
tar -s ",^\+,${_pkgdir}/+," \
-s ",^firmware,${DESTDIR}/etc/firmware," \
-C / -zxphf - "+*" "firmware/*"
# TODO: Should we mark these so real fw_update can -Drepair?
ed -s "${_pkgdir}/+CONTENTS" <<EOL
/^@comment pkgpath/ -1a
@option manual-installation
@option firmware
@comment install-script
.
w
EOL
}
delete_firmware() {
local _cwd _pkg="$1" _pkgdir="${DESTDIR}/var/db/pkg"
# TODO: Check hash for files before deleting
echo "Uninstalling $_pkg"
_cwd="${_pkgdir}/$_pkg"
set -A _remove -- "${_cwd}/+CONTENTS" "${_cwd}"
while read -r c g; do
case $c in
@cwd) _cwd="${DESTDIR}$g"
;;
@*) continue
;;
*) set -A _remove -- "$_cwd/$c" "${_remove[@]}"
;;
esac
done < "${_pkgdir}/${_pkg}/+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
}
usage() {
echo "usage: fw_install [-DL] [driver | file [...]]"
exit 2
}
INSTALL=true
DOWNLOAD=true
while getopts DL name
do
case "$name" in
# "download only" means local dir and don't install
D) LOCALSRC=. INSTALL=false ;;
L) LOCALSRC=. ;;
?) usage 2 ;;
esac
done
shift $((OPTIND - 1))
# If we're installing from a local dir
# we don't want to download anything
[ "$LOCALSRC" ] && "$INSTALL" && DOWNLOAD=false
[ "$LOCALSRC" ] || LOCALSRC="$( tmpdir "${DESTDIR}/tmp/fw_install" )"
CFILE="$LOCALSRC/$CFILE"
set -A devices -- "$@"
[ "${devices[*]:-}" ] ||
set -A devices -- $( devices_needing_firmware )
if [ ! "${devices[*]:-}" ]; then
echo "No devices found which need firmware files to be downloaded."
exit
fi
if "$DOWNLOAD"; then
fetch "$CFILE"
! signify -qVep "$FWPUB_KEY" -x "$CFILE" -m "$CFILE" &&
echo "Signature check of SHA256.sig failed" >&2 && exit 1
fi
for f in "${devices[@]}"; do
d="$( firmware_devicename "$f" )"
if [ "$f" = "$d" ]; then
f=$( firmware_filename "$d" || true )
[ "$f" ] || continue
f="$LOCALSRC/$f"
elif ! "$INSTALL" && ! grep -Fq "($f)" "$CFILE" ; then
echo "Cannot download local file $f" >&2
exit 2
fi
set -A installed -- $( installed_firmware "$d" )
if "$INSTALL" && [ "${installed[*]:-}" ]; then
for i in "${installed[@]}"; do
if [ "${f##*/}" = "$i.tgz" ]; then
echo "$i already installed"
continue 2
fi
done
fi
if [ -e "$f" ]; then
if "$DOWNLOAD"; then
echo "Verify existing ${f##*/}"
verify "$f" || continue
# else assume it was verified when downloaded
fi
elif "$DOWNLOAD"; then
fetch "$f" || continue
verify "$f" || continue
elif "$INSTALL"; then
echo "Cannot install ${f##*/}, not found" >&2
continue
fi
"$INSTALL" || continue
if [ "${installed[*]:-}" ]; then
for i in "${installed[@]}"; do
delete_firmware "$i"
done
fi
add_firmware "$f"
done