diff --git a/board/common/overlay/sbin/fwupdate b/board/common/overlay/sbin/fwupdate old mode 100644 new mode 100755 index a601bfd4ce..63b489b573 --- a/board/common/overlay/sbin/fwupdate +++ b/board/common/overlay/sbin/fwupdate @@ -1,16 +1,35 @@ #!/bin/bash + +#### usage #### + function exit_usage() { - echo "Usage: $0 versions" - echo " $0 current" - echo " $0 download " - echo " $0 extract" - echo " $0 flashboot" - echo " $0 flashreboot" - echo " $0 status" - - - exit -1 + echo "Usage: fwupdate versions" + echo " - lists available versions" + echo " fwupdate current" + echo " - shows the current version" + echo " fwupdate download " + echo " - downloads a firmware version" + echo " fwupdate extract" + echo " - extracts the downloaded firmware archive" + echo " fwupdate flashboot" + echo " - flashes the extracted boot partition" + echo " fwupdate flashreboot" + echo " - prepares for reboot + root partititon flash" + echo " fwupdate status" + echo " - shows the current firmware updating status (see below)" + echo "" + echo "Statuses:" + echo " idle" + echo " downloading : %" + echo " downloaded " + echo " extracting " + echo " extracted " + echo " flashing boot " + echo " boot ready " + echo "" + + exit 1 } if [ -z "$1" ]; then @@ -18,22 +37,48 @@ if [ -z "$1" ]; then fi +#### configuration #### + SYS_VERSION_FILE=/etc/version SYS_BOARD_FILE=/etc/board OS_CONF=/data/etc/os.conf +BOOT_DEV=/dev/mmcblk0p1 +MIN_FREE_DISK=$((500*1024)) # 500 MB +VER_FILE=version +ROOT_INFO_FILE=root_info + +FW_DIR=/data/.fwupdate +FW_FILE=firmware.img.gz +FW_FILE_EXTR=firmware.img + +WGET_LOG_FILE=wget.log +WGET_PID_FILE=wget.pid + +GUNZIP_LOG_FILE=gunzip.log +GUNZIP_PID_FILE=gunzip.pid + +DD_LOG_FILE=dd.log +DD_PID_FILE=dd.pid + + +#### versions #### function show_versions() { source $OS_CONF board=$(cat $SYS_BOARD_FILE) + show_url=$1 # the /usr/libexec/list-versions-* helpers return a table with the following format: # ||| versions=$(FW_USERNAME=$os_firmware_username FW_PASSWORD=$os_firmware_password \ /usr/libexec/list-versions-$os_firmware_method $os_firmware_repo) - for version in $versions: - $IFS="|" varr=($version) + for version in $versions; do + OIFS=$IFS + IFS="|" + varr=($version) + IFS=$OIFS if [ "$os_prereleases" == "false" ] && [ "${varr[1]}" == "true" ]; then continue # skip prereleases fi @@ -41,52 +86,255 @@ function show_versions() { continue # skip other boards fi - echo "${varr[0]}" + if [ "$show_url" == "true" ]; then + echo ${varr[0]} ${varr[3]} + else + echo ${varr[0]} + fi done } function show_current() { source $SYS_VERSION_FILE - echo "Name: $os_name" - echo "Short Name: $os_short_name" - echo "Prefix: $os_prefix" - echo "Version: $os_version" + echo $os_version } + +#### download #### + function do_download() { - # TODO check space - # TODO cleanup download dir - # TODO prepare download dir - # TODO wget --no-check-certificate + echo "downloading..." + + source $OS_CONF + board=$(cat $SYS_BOARD_FILE) + url=$1 + version=$1 + + if ! [[ "$url" == http* ]]; then # a version was given + url=$(show_versions true | grep "^$1" | cut -d ' ' -f 2) + else + version="custom" + fi + + if [ -z "$url" ]; then + echo "no such version" 1>&2 + exit 1 + fi + + free_disk=$(df /data | tail -n 1 | tr -s ' ' | cut -d ' ' -f 4) + if [ "$free_disk" -lt $MIN_FREE_DISK ]; then + echo "not enough disk space" 1>&2 + exit 1 + fi + + rm -rf $FW_DIR/* + mkdir -p $FW_DIR + echo $version > $FW_DIR/$VER_FILE + + wget --no-check-certificate -O $FW_DIR/$FW_FILE --quiet --show-progress --progress=dot "$url" &> $FW_DIR/$WGET_LOG_FILE & + pid=$! + echo $pid > $FW_DIR/$WGET_PID_FILE + wait $pid } +function download_status() { + if [ -f $FW_DIR/$WGET_PID_FILE ]; then + pid=$(cat $FW_DIR/$WGET_PID_FILE) + if kill -0 $pid &>/dev/null; then + progress=$(tail -n2 $FW_DIR/$WGET_LOG_FILE | grep -oe '[[:digit:]]*%') + if [ -z "$progress" ]; then + progress="0%" + fi + + progress=($progress) + + echo ${progress[0]} + return + fi + fi + + if [ -f $FW_DIR/$FW_FILE ]; then + echo "done" + fi +} + + +#### extract #### + function do_extract() { - # TODO gunzip + echo "extracting..." + + if ! [ -f $FW_DIR/$FW_FILE ]; then + echo "firmware file not downloaded" 1>&2 + exit 1 + fi + + rm -f $FW_DIR/$FW_FILE_EXTR + + gunzip -k -c $FW_DIR/$FW_FILE > $FW_DIR/$FW_FILE_EXTR 2>$FW_DIR/$GUNZIP_LOG_FILE & + pid=$! + echo $pid > $FW_DIR/$GUNZIP_PID_FILE + wait $pid + # TODO verify hash } -function flash_boot() { - # TODO backup /boot/config.txt - # TODO umount /boot - # TODO set trap exit cleanup routine - # TODO disable rebooting (/sbin/reboot) - # TODO remount rw / - # TODO read partition table - # TODO dd boot.img - # TODO mount r/w /boot - # TODO restore /boot/config.txt - # TODO call cleanup routine +function extract_status() { + if [ -f $FW_DIR/$GUNZIP_PID_FILE ]; then + pid=$(cat $FW_DIR/$GUNZIP_PID_FILE) + if kill -0 $pid &>/dev/null; then + echo "running" + fi + fi + + if [ -f $FW_DIR/$FW_FILE_EXTR ]; then + echo "done" + fi } + +#### flash boot #### + +function flash_boot() { + echo "flashing boot..." + + set +e + board=$(cat $SYS_BOARD_FILE) + + cp -r /boot $FW_DIR/old_boot + umount /boot + trap flash_cleanup EXIT + + mount -o remount,rw / + mv /sbin/reboot /sbin/reboot.bak + ln -s /bin/true /sbin/reboot + + boot_info=$(fdisk --bytes -l -o device,start,end,size $FW_DIR/$FW_FILE_EXTR | grep "${FW_FILE_EXTR}1") + boot_info=($boot_info) + boot_start=$((${boot_info[1]} * 512)) # in bytes + boot_size=$((${boot_info[3]} / 1048576)) # in MB + + root_info=$(fdisk --bytes -l -o device,start,end,size $FW_DIR/$FW_FILE_EXTR | grep "${FW_FILE_EXTR}2") + root_info=($root_info) + root_start=$((${root_info[1]} * 512)) # in bytes + root_size=$((${root_info[3]} / 1048576)) # in MB + + echo $root_start $root_size > $FW_DIR/$ROOT_INFO + + dd if=$FW_DIR/$FW_FILE_EXTR skip=$boot_start of=$BOOT_DEV bs=1048576 count=$boot_size &>$FW_DIR/$DD_LOG_FILE & + pid=$! + echo $pid > $FW_DIR/$DD_PID_FILE + wait $pid + + mount -o rw /boot + restore_boot_$board $FW_DIR/old_boot 2>/dev/null || true + touch $FW_DIR/boot_flash_ready +} + +function flash_boot_status() { + if [ -f $FW_DIR/$DD_PID_FILE ]; then + pid=$(cat $FW_DIR/$DD_PID_FILE) + if kill -0 $pid &>/dev/null; then + echo "running" + return + fi + fi + + if [ -f $FW_DIR/boot_flash_ready ]; then + echo "done" + fi +} + +function flash_cleanup() { + if [ -f /sbin/reboot.bak ]; then + rm -f /sbin/reboot + mv /sbin/reboot.bak /sbin/reboot + fi + + mount /boot 2>/dev/null +} + +function restore_boot_raspberrypi() { + old_boot=$1 + cp $old_boot/config.txt /boot +} + +function restore_boot_raspberrypi2() { + restore_boot_raspberrypi +} + +function restore_boot_raspberrypi3() { + restore_boot_raspberrypi +} + + +#### flash reboot #### + function flash_reboot() { - # TODO sed /boot/config.txt initramfs - # TODO sync - # TODO umount - # TODO busybox reboot || b > sysrq + echo "preparing for reboot..." + + board=$(cat $SYS_BOARD_FILE) + + mount -o remount,rw /boot + prepare_boot_$board + + sync + busybox reboot & + sleep 10 + echo b > /proc/sysrq-trigger + + exit 0 } +function prepare_boot_raspberrypi() { + echo "initramfs fwupdater.gz" >> /boot/config.txt +} + +function prepare_boot_raspberrypi2() { + prepare_boot_raspberrypi +} + +function prepare_boot_raspberrypi3() { + prepare_boot_raspberrypi +} + + +#### status #### + function show_status() { + status=$(flash_boot_status) + if [ "$status" == "running" ]; then + echo "flashing boot $(new_version)" + return + elif [ "$status" == "done" ]; then + echo "boot ready $(new_version)" + return + fi + + status=$(extract_status) + if [ "$status" == "running" ]; then + echo "extracting $(new_version)" + return + elif [ "$status" == "done" ]; then + echo "extracted $(new_version)" + return + fi + + status=$(download_status) + if [ "$status" == "done" ]; then + echo "downloaded $(new_version)" + return + elif [ -n "$status" ]; then + echo "downloading $(new_version): $status" + return + fi + + echo "idle" +} + +function new_version() { + cat $FW_DIR/$VER_FILE } @@ -100,19 +348,22 @@ case "$1" in ;; download) - if [ -z "$2" ]; + if [ -z "$2" ]; then exit_usage fi do_download $2 + show_status ;; extract) do_extract + show_status ;; flashboot) flash_boot + show_status ;; flashreboot) diff --git a/board/common/overlay/usr/libexec/list-versions-bitbucket b/board/common/overlay/usr/libexec/list-versions-bitbucket index e2a76eb078..e2944530b3 100755 --- a/board/common/overlay/usr/libexec/list-versions-bitbucket +++ b/board/common/overlay/usr/libexec/list-versions-bitbucket @@ -6,9 +6,9 @@ if [ -z "$1" ]; then fi opts="-s -S -f" -test -n "$BITBUCKET_USERNAME" && opts+=" --user $BITBUCKET_USERNAME:$BITBUCKET_PASSWORD" +test -n "$FW_USERNAME" && opts+=" --user $FW_USERNAME:$FW_PASSWORD" url=https://api.bitbucket.org/2.0/repositories/$1/downloads?pagelen=100 -jq_expr='.values[] | [{a: .name | capture("[^-]+-(?[^-]+)-(?[^-]+)\\.img\\.?.*"), url: .links.self.href}] | map(.a.c, "false", .a.b, .url) | join("|")' +jq_expr='.values[] | [{a: .name | split("-"), url: .links.self.href}] | map((.a[2] | rtrimstr(".img.gz") | rtrimstr(".img")), "false", .a[1], .url) | join("|")' curl $opts $url | jq --raw-output "$jq_expr" exit ${PIPESTATUS[0]} diff --git a/board/common/overlay/usr/libexec/list-versions-github b/board/common/overlay/usr/libexec/list-versions-github index 479e0eca67..edea55c26c 100755 --- a/board/common/overlay/usr/libexec/list-versions-github +++ b/board/common/overlay/usr/libexec/list-versions-github @@ -6,9 +6,9 @@ if [ -z "$1" ]; then fi opts="-s -S -f" -test -n "$GITHUB_USERNAME" && opts+=" --user $GITHUB_USERNAME:$GITHUB_PASSWORD" +test -n "$FW_USERNAME" && opts+=" --user $FW_USERNAME:$FW_PASSWORD" url=https://api.github.com/repos/$1/releases -jq_expr='.[] | {version: .name, prerelease: .prerelease | tostring, name: .assets[].name | capture("[^-]+-(?[^-]+)") | flatten, asset: .assets[].browser_download_url} | flatten | join("|")' +jq_expr='.[] | {version: .name, prerelease: .prerelease | tostring} + (.assets[] | {name: .name | split("-")[1], url: .browser_download_url}) | flatten | join("|")' curl $opts $url | jq --raw-output "$jq_expr" exit ${PIPESTATUS[0]}