#!/bin/sh if test $(uci get autoupdater.settings.enabled) != 1; then echo "autoupdater is disabled" exit 0 fi BRANCH=$(uci get autoupdater.settings.branch) PROBABILITY=$(uci get autoupdater.${BRANCH}.probability) if test "a$1" != "a-f"; then # get one random byte from /dev/urandom, convert it to decimal and check # against update_probability*255 hexdump -n1 -e '/1 "%d"' /dev/urandom | awk "{exit \$1 > $PROBABILITY * 255}" if test $? -ne 0; then echo "No autoupdate this time. Use -f to override" exit 0 fi fi BRANCH_NAME=$(uci get autoupdater.${BRANCH}.name) MIRRORS=$(for mirror in $(uci get autoupdater.${BRANCH}.mirror); do \ hexdump -n1 -e '/1 "%d '"$mirror"'\n"' /dev/urandom; \ done | sort -n | cut -d' ' -f2) PUBKEYS=$(uci get autoupdater.${BRANCH}.pubkey) GOOD_SIGNATURES=$(uci get autoupdater.${BRANCH}.good_signatures) VERSION_FILE=/lib/gluon/release newer_than() { local old="$(printf '%s\n%s\n' "$1" "$2" | sort -n | head -n 1)" test "$1" != "$old" } fetch_manifest() { local MIRROR=$1 local manifest=$2 wget -O$manifest "$MIRROR"/manifest if test $? -ne 0; then echo "Couldn't fetch manifest from $MIRROR" >&2 return 1 fi return 0 } verify_manifest() { local manifest=$1 local manifest_upper=$2 local manifest_lower=$(mktemp) awk "BEGIN { sep=0 } /^---\$/ { sep=1; next } { if(sep==0) print > \"$manifest_upper\"; else print > \"$manifest_lower\"}" \ $manifest local signatures="" while read sig; do echo "$sig" | grep -q "^[0-9a-f]\{128\}$" if test $? -ne 0; then continue fi signatures="$signatures -s $sig" done < $manifest_lower local pubkeys="" for key in $PUBKEYS; do pubkeys="$pubkeys -p $key" done rm -f $manifest_lower ecdsaverify -n $GOOD_SIGNATURES $pubkeys $signatures $manifest_upper if test $? -ne 0; then echo "Not enough valid signatures!" >&2 return 1 fi return 0 } analyse_manifest() { local manifest_upper=$1 grep -q "^BRANCH=${BRANCH_NAME}$" $manifest_upper if test $? -ne 0; then echo "Wrong branch. We are on ${BRANCH_NAME}" >&2 return 1 fi local my_firmware my_firmware=$(grep "^${my_model} " $manifest_upper) if test $? -ne 0; then echo "No matching firmware found (model ${my_model})" >&2 return 1 fi fw_version=$(echo "${my_firmware}"|cut -d' ' -f2) fw_checksum=$(echo "${my_firmware}"|cut -d' ' -f3) fw_file=$(echo "${my_firmware}"|cut -d' ' -f4) return 0 } fetch_firmware() { local MIRROR=$1 local fw_image=$2 wget -O$fw_image "${MIRROR}/${fw_file}" if test $? -ne 0; then echo "Error downloading image from $MIRROR" >&2 return 1 fi return 0 } autoupdate() { local MIRROR=$1 local manifest=$(mktemp) fetch_manifest $MIRROR $manifest || { rm -f $manifest; return 1; } local manifest_upper=$(mktemp) verify_manifest $manifest $manifest_upper || { rm -f $manifest $manifest_upper; return 1; } rm -f $manifest analyse_manifest $manifest_upper || { rm -f $manifest_upper; return 1; } rm -f $manifest_upper if newer_than "$fw_version" "$my_version"; then echo "New version available" # drop caches to make room for firmware image sync sysctl -w vm.drop_caches=3 local fw_image=$(mktemp) fetch_firmware $MIRROR $fw_image || { rm -f $fw_image; return 1; } image_sha512=$(sha512sum "$fw_image" | awk '{print $1}') image_md5=$(md5sum "$fw_image" | awk '{print $1}') if [ "$image_sha512" != "$fw_checksum" -a "$image_md5" != "$fw_checksum" ]; then echo "Invalid image checksum" >&2 rm -f $fw_image return 1 fi echo "Upgrading firmware." sysupgrade "${fw_image}" else echo "No new firmware available" fi return 0 } trap 'echo Signal ignored.' INT TERM PIPE . /lib/gluon/functions/model.sh my_model="$(get_model | tr '[A-Z]' '[a-z]' | sed -r 's/[^a-z0-9]+/-/g;s/-$//')" if [ ! -f "$VERSION_FILE" ]; then echo "Couldn't determine firmware version!" >&2 exit 1 fi my_version="$(cat "$VERSION_FILE")" for mirror in $MIRRORS; do autoupdate $mirror && exit 0 unset fw_version unset fw_checksum unset fw_file done