[BACK]Return to fw_install.sh CVS log [TXT][DIR] Up to [local] / openbsd / fw_update

File: [local] / openbsd / fw_update / fw_install.sh (download)

Revision 1.9, Sat Oct 16 18:42:43 2021 UTC (2 years, 8 months ago) by afresh1
Branch: MAIN
Changes since 1.8: +3 -3 lines

Fix more variable names

#!/bin/ksh
set -e

scan_dmesg() {
	# no bsort for now
	sed -n "$1" /var/run/dmesg.boot
}

installed_firmware() {
	for fw in ${PKGDIR}/$1-firmware*; do
		[ -e "$fw" ] || continue
		echo ${fw##*/}
	done
}

# 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 "$@"
}

_issue=
fail() {
	echo $_issue >&2
	exit 1
}

VNAME=$(sysctl -n kern.osrelease)
VERSION="${VNAME%.*}${VNAME#*.}"
FWDIR="$VNAME"

HTTP_FWDIR=$FWDIR
set -- $(scan_dmesg "/^OpenBSD $VNAME\([^ ]*\).*$/s//\1/p")
[[ $1 == -!(stable) ]] && HTTP_FWDIR=snapshots

FWURL=http://firmware.openbsd.org/firmware/${HTTP_FWDIR}
FWPUB_KEY=${DESTDIR}/etc/signify/openbsd-${VERSION}-fw.pub
PKGDIR=${DESTDIR}/var/db/pkg
PATTERNS="file:${0%/*}/firmware_patterns"

drivers=$(
	last=''
	ftp -D "Detecting" -Vmo- $PATTERNS |
	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
)

if [ -z "$drivers" ]; then
	echo "No devices found which need firmware files to be downloaded." >&2
	exit 0
fi

_src=$FWURL
if _tmpsrc=$( tmpdir "${DESTDIR}/tmp/fw_update" ); then
	(
	>$_tmpsrc/t &&
	$_unpriv cat $_tmpsrc/t
	) >/dev/null 2>&1 ||
	    rm -r $_tmpsrc
fi

[[ ! -d $_tmpsrc ]] &&
	_issue="Cannot create prefetch area" && fail

cd "$_tmpsrc"

_t=Get
_cfile="$_tmpsrc/SHA256"
_srclocal=false

! $_unpriv ftp -D "$_t" -Vmo - "$_src/SHA256.sig" >"$_cfile.sig" &&
    _issue="Cannot fetch SHA256.sig" && fail

# Verify signature file with public keys.
! unpriv -f "$_cfile" \
    signify -Vep $FWPUB_KEY -x "$_cfile.sig" -m "$_cfile" &&
    _issue="Signature check of SHA256.sig failed" && fail

for d in $drivers; do
	_f=$( sed -n "s/.*(\($d-firmware-.*\.tgz\)).*/\1/p" "$_cfile" )
	installed=$( installed_firmware "$d" )

	for i in $installed; do
		if [ "$_f" = "$i.tgz" ]; then
			echo "Firmware for $d already installed ($installed)"
			continue 2
		fi
	done

	rm -f /tmp/h /tmp/fail

	_t=Get/Verify
	# 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"
		_issue="Fetching of $_f failed!"
		fail
	fi

	# Verify firmware by comparing its checksum with SHA256.
	if fgrep -qx "SHA256 ($_f) = $(</tmp/h)" "$_cfile"; then
		#_unver=$(rmel $_f $_unver)
		true
	else
		[[ -d "$_tmpsrc" ]] && rm -rf "$_tmpsrc"
		_issue="Checksum test for $_f failed."
		fail
	fi

	# TODO: Check hash for files before deleting
	if [ "$installed" ] && [ -e "${PKGDIR}/$installed/+CONTENTS" ]; then
		echo "Uninstalling $installed"
		cwd=${PKGDIR}/$installed

		remove="${cwd}/+CONTENTS ${cwd}"

		while read c g; do
			case $c in
			@cwd) cwd=$g
			  ;;
			@*) continue
			  ;;
			*)  remove="$cwd/$c $remove"
			  ;;
			esac
		done < "${PKGDIR}/$installed/+CONTENTS"

		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: Add some details about the install to +CONTENTS like pkg_add
	# TODO: Or, maybe we save the firmware someplace and make pkg_add reinstall
	echo "Installing $_f"
	tar -zxphf "$_f" -C /etc "firmware/*"
	mkdir -p ${PKGDIR}/${_f%.tgz}/
	tar -zxphf "$_f" -C "${PKGDIR}/${_f%.tgz}" "+*"
	ed -s "${PKGDIR}/${_f%.tgz}/+CONTENTS" <<EOL
/^@comment pkgpath/ -1a
@option manual-installation
@option firmware
@comment install-script
.
w
EOL
done