fwupdate script is almost functional

This commit is contained in:
Calin Crisan 2017-02-11 13:19:14 +02:00
parent a9b69e0505
commit d79f4e0f00
3 changed files with 293 additions and 42 deletions

327
board/common/overlay/sbin/fwupdate Normal file → Executable file
View File

@ -1,16 +1,35 @@
#!/bin/bash #!/bin/bash
#### usage ####
function exit_usage() { function exit_usage() {
echo "Usage: $0 versions" echo "Usage: fwupdate versions"
echo " $0 current" echo " - lists available versions"
echo " $0 download <version|url>" echo " fwupdate current"
echo " $0 extract" echo " - shows the current version"
echo " $0 flashboot" echo " fwupdate download <version|url>"
echo " $0 flashreboot" echo " - downloads a firmware version"
echo " $0 status" echo " fwupdate extract"
echo " - extracts the downloaded firmware archive"
echo " fwupdate flashboot"
exit -1 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 <version>: <percent>%"
echo " downloaded <version>"
echo " extracting <version>"
echo " extracted <version>"
echo " flashing boot <version>"
echo " boot ready <version>"
echo ""
exit 1
} }
if [ -z "$1" ]; then if [ -z "$1" ]; then
@ -18,22 +37,48 @@ if [ -z "$1" ]; then
fi fi
#### configuration ####
SYS_VERSION_FILE=/etc/version SYS_VERSION_FILE=/etc/version
SYS_BOARD_FILE=/etc/board SYS_BOARD_FILE=/etc/board
OS_CONF=/data/etc/os.conf 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() { function show_versions() {
source $OS_CONF source $OS_CONF
board=$(cat $SYS_BOARD_FILE) board=$(cat $SYS_BOARD_FILE)
show_url=$1
# the /usr/libexec/list-versions-* helpers return a table with the following format: # the /usr/libexec/list-versions-* helpers return a table with the following format:
# <version>|<prerelease>|<board>|<url> # <version>|<prerelease>|<board>|<url>
versions=$(FW_USERNAME=$os_firmware_username FW_PASSWORD=$os_firmware_password \ versions=$(FW_USERNAME=$os_firmware_username FW_PASSWORD=$os_firmware_password \
/usr/libexec/list-versions-$os_firmware_method $os_firmware_repo) /usr/libexec/list-versions-$os_firmware_method $os_firmware_repo)
for version in $versions: for version in $versions; do
$IFS="|" varr=($version) OIFS=$IFS
IFS="|"
varr=($version)
IFS=$OIFS
if [ "$os_prereleases" == "false" ] && [ "${varr[1]}" == "true" ]; then if [ "$os_prereleases" == "false" ] && [ "${varr[1]}" == "true" ]; then
continue # skip prereleases continue # skip prereleases
fi fi
@ -41,52 +86,255 @@ function show_versions() {
continue # skip other boards continue # skip other boards
fi fi
echo "${varr[0]}" if [ "$show_url" == "true" ]; then
echo ${varr[0]} ${varr[3]}
else
echo ${varr[0]}
fi
done done
} }
function show_current() { function show_current() {
source $SYS_VERSION_FILE source $SYS_VERSION_FILE
echo "Name: $os_name" echo $os_version
echo "Short Name: $os_short_name"
echo "Prefix: $os_prefix"
echo "Version: $os_version"
} }
#### download ####
function do_download() { function do_download() {
# TODO check space echo "downloading..."
# TODO cleanup download dir
# TODO prepare download dir source $OS_CONF
# TODO wget --no-check-certificate 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() { 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 # TODO verify hash
} }
function flash_boot() { function extract_status() {
# TODO backup /boot/config.txt if [ -f $FW_DIR/$GUNZIP_PID_FILE ]; then
# TODO umount /boot pid=$(cat $FW_DIR/$GUNZIP_PID_FILE)
# TODO set trap exit cleanup routine if kill -0 $pid &>/dev/null; then
# TODO disable rebooting (/sbin/reboot) echo "running"
# TODO remount rw / fi
# TODO read partition table fi
# TODO dd boot.img
# TODO mount r/w /boot if [ -f $FW_DIR/$FW_FILE_EXTR ]; then
# TODO restore /boot/config.txt echo "done"
# TODO call cleanup routine 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() { function flash_reboot() {
# TODO sed /boot/config.txt initramfs echo "preparing for reboot..."
# TODO sync
# TODO umount board=$(cat $SYS_BOARD_FILE)
# TODO busybox reboot || b > sysrq
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() { 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) download)
if [ -z "$2" ]; if [ -z "$2" ]; then
exit_usage exit_usage
fi fi
do_download $2 do_download $2
show_status
;; ;;
extract) extract)
do_extract do_extract
show_status
;; ;;
flashboot) flashboot)
flash_boot flash_boot
show_status
;; ;;
flashreboot) flashreboot)

View File

@ -6,9 +6,9 @@ if [ -z "$1" ]; then
fi fi
opts="-s -S -f" 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 url=https://api.bitbucket.org/2.0/repositories/$1/downloads?pagelen=100
jq_expr='.values[] | [{a: .name | capture("[^-]+-(?<b>[^-]+)-(?<c>[^-]+)\\.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" curl $opts $url | jq --raw-output "$jq_expr"
exit ${PIPESTATUS[0]} exit ${PIPESTATUS[0]}

View File

@ -6,9 +6,9 @@ if [ -z "$1" ]; then
fi fi
opts="-s -S -f" 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 url=https://api.github.com/repos/$1/releases
jq_expr='.[] | {version: .name, prerelease: .prerelease | tostring, name: .assets[].name | capture("[^-]+-(?<b>[^-]+)") | 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" curl $opts $url | jq --raw-output "$jq_expr"
exit ${PIPESTATUS[0]} exit ${PIPESTATUS[0]}