#!/bin/bash # # RESUME DOWNLOAD DETAILS # swupdate can retry and resume a broken download. See -t and -r arguments # in do_swupdate call at end of this file. # # Implementation in swupdate: # https://github.com/sbabic/swupdate/blob/master/corelib/downloader.c # # Note that it only resumes the download when kept running: after the # swupdate process has stopped, for example because of a reboot, it will # restart. Improving this, without adding an intermediate scratchpad on disk, # is not straightforward: the download is streamed straight into ubifs on the # CCGX, and re-opening an unfinished ubifs volume is not a good idea. # # Resuming while online file has changed: # When a new version is made available, the venus-swu-[machine].swu file on # the webserver is replaced with the newer one. Devices busy starting a # resume after that should not accidentally resume the download with the new # file. A waste of bandwidth and possible leads to installing and booting # into a corrupt rootfs. (What type of CRC or hash does swupdate on the swu # file after download?) # # This is prevented this by dowloading the file that contains its version # in the name. Therefore the webserver should always have the latest file # available under two names: # venus-swu-[machine].swu # venus-swu-[machine]-[build-date-time].swu # # Best sequence of installing the files on the webserver is: # 1. venus-swu-[machine]-[build-date-time].swu # 2. venus-swu-[machine].swu # 3. rename or remove the old build-date-time file: force the running # downloads to cancel. . $(dirname $0)/functions.sh get_setting() { dbus-send --print-reply=literal --system --type=method_call \ --dest=com.victronenergy.settings \ "/Settings/System/$1" \ com.victronenergy.BusItem.GetValue | awk '{ print $3 }' } get_swu_version() { if [ -f "$1" ]; then # local file cmd="head -n 10" else # url, probably cmd="curl -s -r 0-999 -m 30 --retry 3" fi $cmd "$1" | cpio --quiet -i --to-stdout sw-description 2>/dev/null | sed -n '/venus-version/ { s/.*"\(.*\)".*/\1/ p q }' } swu_status() { printf '%s\n' "$1" ${offline:+""} "$2" >$status_file } status_file=/var/run/swupdate-status start_log echo "*** Checking for updates ***" echo "arguments: $@" while [[ $# -gt 0 ]]; do case "$1" in -auto) update=auto auto=1 ;; -check) update=2 ;; -update) update=1 ;; -delay) delay=y ;; -force) force=y ;; -swu) shift force=y forceswu="$1" update="1" ;; -swubase) shift swubase="$1" ;; -offline)offline=y ;; -help) help=y ;; *) echo "Invalid option $arg" exit 1 ;; esac shift done if [ "$help" = y ]; then echo "check-updates.sh: wrapper script around swupdate" echo echo "Arguments:" echo "-auto script will check automatic update setting in localsettings." echo " use this when calling from cron or after boot." echo "-delay sleep for a random delay before starting the download of" echo " new image (to prevent thousands of units starting the" echo " download at the same time)." echo " use this when calling from cron or after boot." echo "-check (only) check if there is a new version available." echo "-update check and, when necessary, update." echo "-force force downloading and installing the new image, even if its" echo " version is older or same as already installed version." echo "-swu url forcefully install the swu from given url" echo "-swubase url use given url as a base, rather than the default:" echo " https://updates.victronenergy.com/feeds/venus/[feed]/" echo "-offline search for updates on removable storage devices" echo "-help this help" echo echo "Behaviour when called without any arguments is same as -update" exit fi if [ "${update:-auto}" = auto ]; then update=$(get_setting AutoUpdate) case $update in 0) echo "Auto-update disabled, exit." exit ;; 1) ;; 2) ;; *) echo "Invalid AutoUpdate value $update, exit." exit 1 ;; esac fi if [ "$delay" = y ]; then DELAY=$[ $RANDOM % 3600 ] echo "Sleeping for $DELAY seconds" sleep $DELAY fi machine=$(cat /etc/venus/machine) swu_name=$(cat /etc/venus/swu-name) swu_base=${swu_name}-${machine} if [[ $forceswu ]]; then echo "Updating to $forceswu" SWU="$forceswu" # The version is not known, since the stream might not support seeking, # like stdin for example. So as a best effort, use the url instead. swu_version="$forceswu" elif [ "$offline" = y ]; then echo "Searching for update on SD/USB..." for dev in /media/*; do # reverse order gives preference to an unversioned file # followed by that with the most recent timestamp if # multiple files exist # # MIND IT: There are ccgx and venusgx around which only check for # venus-swu-${machine}*.swu so don't make an incompatible ccgxv2 or # beaglebone-new MACHINE, since they are also accepted by the old ones. SWU=$(ls -r $dev/${swu_base}-*.swu $dev/${swu_base}.swu 2>/dev/null | head -n1) test -f "$SWU" && break done if [ -f "$SWU" ]; then echo "Update found on $dev" feed="$dev" else echo "Update not found. Exit." swu_status -3 exit 1 fi else feed=$(get_setting ReleaseType) case $feed in 0) feed=release ;; 1) feed=candidate ;; 2) feed=testing ;; 3) feed=develop ;; *) echo "Invalid release type, exit." exit 1 ;; esac if [ -z "$swubase" ]; then swubase=https://static.innovenergy.ch/firmware/venus/${feed}/ fi URL_BASE=${swubase}images/${machine} SWU=${URL_BASE}/${swu_base}.swu fi if [[ -z $forceswu ]]; then echo "Retrieving latest version... (from $SWU)" swu_status 1 cur_version=$(get_version) swu_version=$(get_swu_version "$SWU") if [ -z "$swu_version" ]; then echo "Unable to retrieve latest software version, exit." swu_status -1 exit 1 fi cur_build=${cur_version%% *} swu_build=${swu_version%% *} if [ "$offline" != y ]; then # change SWU url into the full name SWU=${URL_BASE}/${swu_base}-${swu_version// /-}.swu fi echo "installed: $cur_version" echo "available: $swu_version" if [ "$force" != y -a \( "${swu_build}" = "${cur_build}" -o "$auto" = "1" -a "${swu_build}" -le "${cur_build}" \) ]; then echo "No newer version available, exit." swu_status 0 exit fi if [ "$update" != 1 ]; then swu_status 0 "$swu_version" exit fi fi altroot=$(get_altrootfs) if [ -z "$altroot" ]; then echo "Unable to determine rootfs. Exit." swu_status -2 "$swu_version" exit 1 fi if ! lock; then echo "Can't get lock, other process already running? Exit." swu_status 0 "$swu_version" exit 1 fi echo "Starting swupdate to install version $swu_version ..." swu_status 2 "$swu_version" # backup rootfs is about to be replaced, remove its version entry get_version >/var/run/versions if [ -f "$SWU" ]; then swupdate_flags="-i" else swupdate_flags="-t 30 -r 3 -d" fi if do_swupdate -v $swupdate_flags "$SWU" -e "stable,copy$altroot"; then echo "do_swupdate completed OK. Rebooting" swu_status 3 "$swu_version" reboot else echo "Error, do_swupdate stopped with exitcode $?, unlock and exit." swu_status -2 "$swu_version" fi unlock