Here's a slightly simpler try suggested by deraadt@. I capture ftp's STDERR and move it to the special "WARN_FD" that groups errors without confusing the status line.
I also strip the "name" off the error as it looks a bit nicer than repeating it everywhere. If the fetch fails, we return 1 if we find "404 Not Found" and 2 otherwise. We then can continue on to another file in the 1 case and exit immediately in the 2. We always fail immediately on failures to fetch SHA256.sig as that indicates that we're not going to be able to do much. diff --git a/fw_update.sh b/fw_update.sh index a379d74..8062da0 100644 --- a/fw_update.sh +++ b/fw_update.sh @@ -107,6 +107,9 @@ spin() { fetch() { local _src="${FWURL}/${1##*/}" _dst=$1 _user=_file _exit _error='' + local _name=${0##*/} + local _ftp_errors="$FD_DIR/ftp_errors" + rm -f "$_ftp_errors" # The installer uses a limited doas(1) as a tiny su(1) set -o monitor # make sure ftp gets its own process group @@ -118,12 +121,12 @@ fetch() { esac if [ -x /usr/bin/su ]; then exec /usr/bin/su -s /bin/ksh "$_user" -c \ - "/usr/bin/ftp -N '${0##/}' -D 'Get/Verify' $_flags -o- '$_src'" > "$_dst" + "/usr/bin/ftp -N '$_name' -D 'Get/Verify' $_flags -o- '$_src'" > "$_dst" else exec /usr/bin/doas -u "$_user" \ - /usr/bin/ftp -N "${0##/}" -D 'Get/Verify' $_flags -o- "$_src" > "$_dst" + /usr/bin/ftp -N "$_name" -D 'Get/Verify' $_flags -o- "$_src" > "$_dst" fi - ) & FTPPID=$! + ) 2>"$_ftp_errors" & FTPPID=$! set +o monitor SECONDS=0 @@ -151,10 +154,27 @@ fetch() { unset FTPPID - if [ "$_exit" -ne 0 ]; then + if ((_exit != 0)); then + # ftp doesn't provide useful exit codes + # so we have to grep its STDERR. + # _exit=2 means don't keep trying + _exit=2 + + # If it was 404, it's we might succeed at another file. + if grep -q "404 Not Found" "$_ftp_errors"; then + _exit=1 + _error=" (404 Not Found)" + rm -f "$_ftp_errors" + fi + fi + + [ -s "$_ftp_errors" ] && + sed "s/^$_name: //" "$_ftp_errors" >&"$WARN_FD" + + if ((_exit != 0)); then rm -f "$_dst" warn "Cannot fetch $_src$_error" - return 1 + return "$_exit" fi return 0 @@ -165,12 +185,12 @@ fetch() { # a blank file indicating failure. check_cfile() { if [ -e "$CFILE" ]; then - [ -s "$CFILE" ] || return 1 + [ -s "$CFILE" ] || return 2 return 0 fi if ! fetch_cfile; then echo -n > "$CFILE" - return 1 + return 2 fi return 0 } @@ -192,7 +212,7 @@ fetch_cfile() { } verify() { - check_cfile || return 1 + check_cfile || return $? # The installer sha256 lacks -C, do it by hand if ! grep -Fqx "SHA256 (${1##*/}) = $( /bin/sha256 -qb "$1" )" "$CFILE" then @@ -207,7 +227,7 @@ verify() { # if VERBOSE is 0, don't show the checksum failure of an existing file. verify_existing() { local _v=$VERBOSE - check_cfile || return 1 + check_cfile || return $? ((_v == 0)) && "$DOWNLOAD" && _v=1 ( VERBOSE=$_v verify "$@" ) @@ -242,7 +262,7 @@ firmware_in_dmesg() { } firmware_filename() { - check_cfile || return 1 + check_cfile || return $? sed -n "s/.*(\($1-firmware-.*\.tgz\)).*/\1/p" "$CFILE" | sed '$!d' } @@ -599,7 +619,19 @@ if [ "${devices[*]:-}" ]; then verify_existing=true if [ "$f" = "$d" ]; then - f=$( firmware_filename "$d" ) || continue + f=$( firmware_filename "$d" ) || { + # Fetching the CFILE here is often the + # first attempt to talk to FWURL + # Fetching the CFILE here is often the + # If it does, no point in continuing. + if (($? > 1)); then + status " failed" + exit 1 + fi + + # If it does, no point in continuing. + continue + } if [ ! "$f" ]; then if "$INSTALL" && unregister_firmware "$d"; then unregister="$unregister,$d" @@ -717,11 +749,18 @@ for f in "${add[@]}" _update_ "${update[@]}"; do fi fetch "$f" && verify "$f" || { + integer e=$? + if "$pending_status"; then echo " failed." elif ! ((VERBOSE)); then status " failed (${f##*/})" fi + + # Fetch or verify exited > 1 + # which means we don't keep trying. + ((e > 1)) && exit 1 + continue } fi