autoupdater 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #!/bin/sh
  2. BRANCH=$(uci get autoupdater.settings.branch)
  3. PROBABILITY=$(uci get autoupdater.${BRANCH}.probability)
  4. if test "a$1" != "a-f"; then
  5. if test $(uci get autoupdater.settings.enabled) != 1; then
  6. echo "autoupdater is disabled"
  7. exit 0
  8. fi
  9. # get one random byte from /dev/urandom, convert it to decimal and check
  10. # against update_probability*255
  11. hexdump -n1 -e '/1 "%d"' /dev/urandom | awk "{exit \$1 > $PROBABILITY * 255}"
  12. if test $? -ne 0; then
  13. echo "No autoupdate this time. Use -f to override"
  14. exit 0
  15. fi
  16. fi
  17. BRANCH_NAME=$(uci get autoupdater.${BRANCH}.name)
  18. MIRRORS=$(for mirror in $(uci get autoupdater.${BRANCH}.mirror); do \
  19. hexdump -n1 -e '/1 "%d '"$mirror"'\n"' /dev/urandom; \
  20. done | sort -n | cut -d' ' -f2)
  21. PUBKEYS=$(uci get autoupdater.${BRANCH}.pubkey)
  22. GOOD_SIGNATURES=$(uci get autoupdater.${BRANCH}.good_signatures)
  23. VERSION_FILE=/lib/gluon/release
  24. # returns 0 when $1 is a higher version number than $2
  25. newer_than() {
  26. # negate the return value as opkg returns 1 when the proposition is true
  27. ! opkg compare-versions "$1" '>>' "$2"
  28. }
  29. fetch_manifest() {
  30. local MIRROR=$1
  31. local manifest=$2
  32. wget -O$manifest "$MIRROR"/manifest
  33. if test $? -ne 0; then
  34. echo "Couldn't fetch manifest from $MIRROR" >&2
  35. return 1
  36. fi
  37. return 0
  38. }
  39. verify_manifest() {
  40. local manifest=$1
  41. local manifest_upper=$2
  42. local manifest_lower=$(mktemp)
  43. awk "BEGIN { sep=0 }
  44. /^---\$/ { sep=1; next }
  45. { if(sep==0) print > \"$manifest_upper\";
  46. else print > \"$manifest_lower\"}" \
  47. $manifest
  48. local signatures=""
  49. while read sig; do
  50. echo "$sig" | grep -q "^[0-9a-f]\{128\}$"
  51. if test $? -ne 0; then
  52. continue
  53. fi
  54. signatures="$signatures -s $sig"
  55. done < $manifest_lower
  56. local pubkeys=""
  57. for key in $PUBKEYS; do
  58. pubkeys="$pubkeys -p $key"
  59. done
  60. rm -f $manifest_lower
  61. ecdsaverify -n $GOOD_SIGNATURES $pubkeys $signatures $manifest_upper
  62. if test $? -ne 0; then
  63. echo "Not enough valid signatures!" >&2
  64. return 1
  65. fi
  66. return 0
  67. }
  68. analyse_manifest() {
  69. local manifest_upper=$1
  70. grep -q "^BRANCH=${BRANCH_NAME}$" $manifest_upper
  71. if test $? -ne 0; then
  72. echo "Wrong branch. We are on ${BRANCH_NAME}" >&2
  73. return 1
  74. fi
  75. local my_firmware
  76. my_firmware=$(grep "^${my_model} " $manifest_upper)
  77. if test $? -ne 0; then
  78. echo "No matching firmware found (model ${my_model})" >&2
  79. return 1
  80. fi
  81. fw_version=$(echo "${my_firmware}"|cut -d' ' -f2)
  82. fw_checksum=$(echo "${my_firmware}"|cut -d' ' -f3)
  83. fw_file=$(echo "${my_firmware}"|cut -d' ' -f4)
  84. return 0
  85. }
  86. fetch_firmware() {
  87. local MIRROR=$1
  88. local fw_image=$2
  89. wget -O$fw_image "${MIRROR}/${fw_file}"
  90. if test $? -ne 0; then
  91. echo "Error downloading image from $MIRROR" >&2
  92. return 1
  93. fi
  94. return 0
  95. }
  96. autoupdate() {
  97. local MIRROR=$1
  98. local manifest=$(mktemp)
  99. fetch_manifest $MIRROR $manifest || { rm -f $manifest; return 1; }
  100. local manifest_upper=$(mktemp)
  101. verify_manifest $manifest $manifest_upper || { rm -f $manifest $manifest_upper; return 1; }
  102. rm -f $manifest
  103. analyse_manifest $manifest_upper || { rm -f $manifest_upper; return 1; }
  104. rm -f $manifest_upper
  105. if newer_than "$fw_version" "$my_version"; then
  106. echo "New version available"
  107. # drop caches to make room for firmware image
  108. sync
  109. sysctl -w vm.drop_caches=3
  110. local fw_image=$(mktemp)
  111. fetch_firmware $MIRROR $fw_image || { rm -f $fw_image; return 1; }
  112. image_sha512=$(sha512sum "$fw_image" | awk '{print $1}')
  113. image_md5=$(md5sum "$fw_image" | awk '{print $1}')
  114. if [ "$image_sha512" != "$fw_checksum" -a "$image_md5" != "$fw_checksum" ]; then
  115. echo "Invalid image checksum" >&2
  116. rm -f $fw_image
  117. return 1
  118. fi
  119. echo "Upgrading firmware."
  120. sysupgrade "${fw_image}"
  121. else
  122. echo "No new firmware available"
  123. fi
  124. return 0
  125. }
  126. trap 'echo Signal ignored.' INT TERM PIPE
  127. . /lib/gluon/functions/model.sh
  128. my_model="$(get_model | tr '[A-Z]' '[a-z]' | sed -r 's/[^a-z0-9]+/-/g;s/-$//')"
  129. if [ ! -f "$VERSION_FILE" ]; then
  130. echo "Couldn't determine firmware version!" >&2
  131. exit 1
  132. fi
  133. my_version="$(cat "$VERSION_FILE")"
  134. for mirror in $MIRRORS; do
  135. autoupdate $mirror && exit 0
  136. unset fw_version
  137. unset fw_checksum
  138. unset fw_file
  139. done