From 9d339f44de4037a053b708b5e8739e4ec67d3f28 Mon Sep 17 00:00:00 2001 From: ak2k <19240940+ak2k@users.noreply.github.com> Date: Sat, 25 Apr 2026 22:21:31 -0400 Subject: [PATCH] fix(dfu): harden flash_beacon.sh with timeouts + retries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mcumgr was previously called with no timeout flags and no wall-clock cap. A stuck SMP transfer (BLE link drop, MTU stall, peer reboot mid-upload) would hang the script indefinitely with no useful output. Hardening: - set -euo pipefail (script previously continued past a failed upload to image confirm, which would confirm against potentially stale state) - mcumgr -t 5 -r 5 on every call: 5s per-RPC timeout, 5 retries per packet — covers transient MTU/link issues without masking a real hang - timeout 300s wall-clock cap on the upload step (SIGTERM, then SIGKILL after 10s grace) — 200 KB image at MTU 498 is 30-90s in practice - MCUMGR env var with sane fallback (was hardcoded to ~/go/bin/mcumgr) - UPLOAD_TIMEOUT env var for tuning - bash array variables for connection args (eliminates shell-quoting fragility around the connstring) - Argument validation refactored to use ${VAR:-} so unset args fail cleanly under set -u - sudo -v to prime the credential cache (was: sudo echo "") - shellcheck clean Behavior on success is unchanged. Failure modes that previously hung silently now exit non-zero with a clear cause. --- flash_beacon.sh | 70 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/flash_beacon.sh b/flash_beacon.sh index 50a2715..9ce807f 100755 --- a/flash_beacon.sh +++ b/flash_beacon.sh @@ -1,45 +1,71 @@ -#!/bin/sh +#!/usr/bin/env bash +# +# OTA flash a signed firmware image to a running Everytag beacon over BLE. +# Wraps mcumgr (Go binary) with per-RPC timeouts/retries and a wall-clock +# cap on the upload step so a stuck SMP transfer fails loudly instead of +# hanging forever. -ADDR=`echo "$1"| tr '[:upper:]' '[:lower:]'` -AUTH="$2" -FILE="$3" +set -euo pipefail -if [ "$ADDR" = "" -o "$AUTH" = "" -o "$FILE" = "" ]; then - echo "Usage: ./flash_beacon.sh MAC_ADDR AUTH_KEY FILE_UPLOAD" - exit 1; +ADDR=$(echo "${1:-}" | tr '[:upper:]' '[:lower:]') +AUTH="${2:-}" +FILE="${3:-}" + +if [ -z "$ADDR" ] || [ -z "$AUTH" ] || [ -z "$FILE" ]; then + echo "Usage: ./flash_beacon.sh MAC_ADDR AUTH_KEY FILE_UPLOAD" >&2 + exit 1 fi -echo "BLE MAC: $ADDR" -echo "AUTH: $AUTH" -echo "FILE: $FILE" +MCUMGR="${MCUMGR:-$HOME/go/bin/mcumgr}" +# Per-RPC: 5 s timeout, 5 retries. Each SMP packet (≈MTU 498) gets 5 +# attempts to complete before the whole upload aborts. +MCUMGR_FLAGS=(-t 5 -r 5) +# Wall-clock cap on the full upload (SIGTERM after, SIGKILL +10 s later). +# 200 KB image at MTU 498 ≈ 30–90 s in practice; 5 min covers slow stacks +# and short retries without hiding a true hang. +UPLOAD_TIMEOUT="${UPLOAD_TIMEOUT:-300s}" + +if [ ! -x "$MCUMGR" ]; then + echo "mcumgr not found at $MCUMGR (override with MCUMGR=/path/to/mcumgr)" >&2 + exit 1 +fi + +echo "BLE MAC: $ADDR" +echo "AUTH: $AUTH" +echo "FILE: $FILE" +echo "mcumgr: $MCUMGR ${MCUMGR_FLAGS[*]}" +echo "upload timeout: $UPLOAD_TIMEOUT" echo "" -echo "Requires sudo for for mcumgr" -sudo echo "" +echo "Requires sudo for mcumgr" +sudo -v -echo "Switching to mcumgr mode with write_beacon.py" -python3 ./conn_beacon.py -i "$ADDR" -a "$AUTH" +echo "Switching to mcumgr mode via conn_beacon.py" +python3 ./conn_beacon.py -i "$ADDR" -a "$AUTH" echo "" sleep 1 -HCIDEV=`sudo hcitool dev | tail -n 1 |awk '{print $1;}'|sed s/'^.*\([0-9]\)'/'\1'/` +HCIDEV=$(sudo hcitool dev | tail -n 1 | awk '{print $1}' | sed 's/^.*\([0-9]\)/\1/') + +CONN=(--conntype ble --connstring "peer_id=$ADDR") echo "Checking image list" -sudo ~/go/bin/mcumgr -i "$HCIDEV" --conntype ble --connstring "peer_id=$ADDR" image list +sudo "$MCUMGR" "${MCUMGR_FLAGS[@]}" -i "$HCIDEV" "${CONN[@]}" image list echo "" echo "Erasing unused image (if any)" -sudo ~/go/bin/mcumgr -i "$HCIDEV" --conntype ble --connstring "peer_id=$ADDR" image erase +sudo "$MCUMGR" "${MCUMGR_FLAGS[@]}" -i "$HCIDEV" "${CONN[@]}" image erase echo "" -echo "Uploading new image" -sudo ~/go/bin/mcumgr -i "$HCIDEV" --conntype ble --connstring "peer_id=$ADDR" image upload "$FILE" +echo "Uploading new image (wall-clock cap: $UPLOAD_TIMEOUT)" +sudo timeout --kill-after=10s "$UPLOAD_TIMEOUT" \ + "$MCUMGR" "${MCUMGR_FLAGS[@]}" -i "$HCIDEV" "${CONN[@]}" image upload "$FILE" echo "" echo "Confirming new image" -HASH=`sudo ~/go/bin/mcumgr -i "$HCIDEV" --conntype ble --connstring "peer_id=$ADDR" image list | grep hash |tail -n 1 | awk '{print $2;}'` -sudo ~/go/bin/mcumgr -i "$HCIDEV" --conntype ble --connstring "peer_id=$ADDR" image confirm "$HASH" +HASH=$(sudo "$MCUMGR" "${MCUMGR_FLAGS[@]}" -i "$HCIDEV" "${CONN[@]}" image list | grep hash | tail -n 1 | awk '{print $2}') +sudo "$MCUMGR" "${MCUMGR_FLAGS[@]}" -i "$HCIDEV" "${CONN[@]}" image confirm "$HASH" echo "" #echo "Resetting beacon" -#sudo ~/go/bin/mcumgr -i "$HCIDEV" --conntype ble --connstring "peer_id=$ADDR" reset +#sudo "$MCUMGR" "${MCUMGR_FLAGS[@]}" -i "$HCIDEV" "${CONN[@]}" reset #echo ""