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

Reply via email to