#!/bin/bash
# This script installs the inner tar.gz
# of a fw package. It is intended to be
# packaged in the outer tar of a fw package,
# parallel to the inner tar.gz. The base fwupdate
# script on the target rootfs should
# extract the outer tar and then call 
# this script to do all of the work.

# Exit on any error.
set -e
# Error if an undefined variable is used.
set -u
# Be verbose
set -x

readonly CLEANUP_FILE="$(mktemp)"
# We can only have one trap per signal. Do not use trap after this point.
chmod +x "$CLEANUP_FILE"
trap "fw_setenv fwupdatestatus install_fail; /sbin/reboot" ERR SIGTERM SIGHUP
trap "bash -x '$CLEANUP_FILE'; rm '$CLEANUP_FILE'" 0

cleanup() {
    # Preserve quoting by using sh -c ''. This fails if the command string
    # requires a literal single quote
    echo /bin/bash -c "'$*'" >> "$CLEANUP_FILE"
}

if test -x "$(which blink_led)"
then
    blink_led OPER_LED 100 &
    cleanup "kill $! || true"
fi

# Redirect all stdout and stderr to the serial console, stdout, and a file.
{
    readonly FWUPDATE_LOG=/var/log/fwupdate.log
    readonly LOG_FIFO="$(mktemp -u)"
    readonly SERIAL_CONSOLE="/dev/ttyPS0"
    mkfifo "$LOG_FIFO"
    cleanup "rm -f $LOG_FIFO"

    echo Sending $0 output to "$FWUPDATE_LOG"
    (cat "$LOG_FIFO" | tee "$FWUPDATE_LOG" > "$SERIAL_CONSOLE") &
    cleanup "kill $!"
    exec > "$LOG_FIFO" 2>&1
}

if [ $# -ne 1 ]; then 
    echo "Usage: $0 /path/to/firmware.tar" 
    fw_setenv fwupdatestatus validation_fail
    exit 1
fi

date -u

fatal () {
    echo $0: Error: $*
    fw_setenv fwupdatestatus validation_fail
    exit 1
}

# Make sure that we are done booting before starting the firmware update.
if [ ! -d /usr/boot_done ]; then 
    echo $0: Error: "Cannot start firmware update before boot is complete"

    #Set the update status to not in progress since this is due to
    #an attempt to install a package from usb at startup.
    fw_setenv fwupdatestatus not_in_progress
    exit 0
fi

# Make sure there is no other firmware update in progress
PIDFILE=/tmp/fwupdate.pid
if [ -e $PIDFILE ] && [ -e "/proc/$(cat $PIDFILE)" ]
then fatal "Firmware update already in progress ($(cat $PIDFILE))"
else
    echo -n $$ > $PIDFILE
    cleanup "rm -f $PIDFILE"
fi

# Take the absolute path to the archive before changing directory.
OUTER_ARCHIVE="$(readlink -f "$1")"
[ -e "$OUTER_ARCHIVE" ] || fatal "$OUTER_ARCHIVE: No such file"

# Stop the backendloader watchdog timer before stopping the backendloader
/etc/init.d/bewd stop

# Stop the backend loader before remounting the partition to ensure there is
# no open file handle
/etc/init.d/backendloader stop

# (maybe) format the emmc as early as possible
{
    if tar tf "$OUTER_ARCHIVE" format_emmc.sh
    then
        # Extract format_emmc.sh to RAM because we don't want to assume
        # anything about the filesystem.
        cd /tmp

        if tar xf "$OUTER_ARCHIVE" format_emmc.sh
        then
            if test -x format_emmc.sh
            then ./format_emmc.sh
            else echo "format_emmc.sh is not executable. Not formatting."
            fi
        else
            fatal "Failed extracting format_emmc.sh"
        fi
    fi
}

# Mount the partition with read-write access before updating the firmware
mount -o remount,rw /media/card

# Make sure the temp directory is on the physical SD card, and is empty. This
# is specifically not $(mktemp -d) because that is a directory on the
# RAM-based tempfs.
rm -rf /media/card/.tmp
mkdir -p /media/card/.tmp
cd /media/card/.tmp/
cleanup "rm -rf /media/card/.tmp"

# Open the top-level tar and verify the package checksum. Don't assume
# anything about the filename.
tar xf "$OUTER_ARCHIVE"
ls -l

# Verify the package checksum. Don't assume
# anything about the filename.
SHA1="$(find . -name "*.sha1" -exec basename {} \; | head -n1)"

# Check that the .sha1 file actually exists. If no file was found, the SHA1
# variable will be empty and sha1sum will try to read it from stdin, and the
# program will hang indefinitely.
[ -e "$SHA1" ] || fatal "No sha1sum file found"
sha1sum -c "$SHA1"

# Set up the rest of the script to use the inner tar.gz
INNER_ARCHIVE="$(awk '{print $2}' "$SHA1")"

tar xf "$INNER_ARCHIVE"

# If we extract "foo.tar.gz", expect it to contain a single directory "foo"
PACKAGE_ID="$(basename $INNER_ARCHIVE .tar.gz)"
[ -d "$PACKAGE_ID" ] || fatal "$PACKAGE_ID: No such directory"

cd /media/card/.tmp/"$PACKAGE_ID"

update_fpga() {
    grep -q /media/card /proc/mounts || mount /media/card
    mkdir -p /media/card/fpga

    echo -n "fpga/ ... "
    if [ -d fpga ]
    then
        rm -r /media/card/fpga  
        mv {fpga,/media/card/fpga}
        echo updated
    else
        echo not found
        return 1
    fi
}

update_cpu1() {
    grep -q /media/card /proc/mounts || mount /media/card
    mkdir -p /media/card/cpu1

    echo -n "cpu1/sweeploop_cpu1.bin ... "
    if [ -f cpu1/sweeploop_cpu1.bin ]
    then
        mv {cpu1,/media/card/cpu1}/sweeploop_cpu1.bin
        echo updated
        return 0
    else
        echo not found
        return 1
    fi
}

update_framework() {
    grep -q /media/card /proc/mounts || mount /media/card
    mkdir -p /media/card/framework

    echo -n "framework ... "

    if [ -d framework ]
    then
        # mv can't overwrite an existing tree
        rm -r /media/card/framework
        mv framework /media/card/framework
        echo updated
        return 0
    else
        echo not found
        return 1
    fi
}

update_config() {
    grep -q /mnt/systemroot /proc/mounts || mount /mnt/systemroot
    mkdir -p /mnt/systemroot/config

    echo -n "config ... "

    if [ -d config ]
    then

        for F in config/*
        do
            #Always move update.config file.  It defines update urls locations (non-device specific).
            #Only move the others if they don't exist.  Those are device specific configurations.
            if [ ! -e "/mnt/systemroot/$F" ] || [ "$F" = "config/update.config" ]
            then
                [ -d "$F" ] && rm -r "/mnt/systemroot/$F"
                mv "$F" "/mnt/systemroot/$F"
            fi
        done
        echo updated
        return 0
    else
        echo not found
        return 1
    fi
}

maybe_update_boot_partition() {
    # if blkid is available, we can distinguish between ext2, ext3 and ext4
    if which blkid
    then
        # This pipeline has to deal with different output formats from the same tool,
        # the version in old petalinux fallback images, and the version in Yocto.
        # The former gives everything on one line, e.g. /dev/mmcblk0p1: LABEL="boot" UUID="386D-4567" TYPE="vfat" 
        # The latter has a separate line for each value, e.g. ID_FS_TYPE=vfat
        FS_TYPE="$(blkid -o udev /dev/mmcblk0p1 | grep -E '(FS_)?TYPE' | sed 's/^.*TYPE=//' | tr -d '"')"
        if [ "$FS_TYPE" = "ext3" ] || [ "$FS_TYPE" = "vfat" ]
        then
            echo boot fs is $FS_TYPE, skipping migration
            return
        fi
    else
        # we can only distinguish between ext* or vfat
        mkdir -p /mnt/kernel
        grep -q /mnt/kernel /proc/mounts || mount /dev/mmcblk0p1 /mnt/kernel
        FS_TYPE="$(cat /proc/mounts | grep mmcblk0p1 | awk '{print $3}')"
        umount /dev/mmcblk0p1

        if [ "$FS_TYPE" = "vfat" ]
        then
            echo boot fs is $FS_TYPE, skipping migration
            return
        fi
    fi

    # In case some old image lacks ext3, don't fail the whole upgrade
    if which mkfs.ext3
    then
        echo -n "boot fs is $FS_TYPE, migrating to ext3 ... "
        mkfs.ext3 -t ext3 /dev/mmcblk0p1
        echo done
    fi
}

update_kernel() {
    # migrate filesystem if necessary
    maybe_update_boot_partition

    mkdir -p /mnt/kernel
    # During normal boot, the boot partition is not mounted
    grep -q /mnt/kernel /proc/mounts || mount /dev/mmcblk0p1 /mnt/kernel
    mkdir -p /mnt/kernel/.tmp
    mkdir -p /media/card/bootsupport

    # Remove the old image.ub first so we don't risk running out of space on the boot partition
    rm -f /mnt/kernel/image.ub

    echo -n "image.ub ... "
    RET=0

    if [ -f os/image.ub ]
    then
        cp os/image.ub /mnt/kernel/.tmp/image.ub
        mv /mnt/kernel/{.tmp,}/image.ub
        echo updated
    else
        echo not found
        RET=1
    fi

    echo -n "bootsupport ... "
    if [ -d bootsupport ]
    then
        # mv can't overwrite an existing tree
        rm -r /media/card/bootsupport
        mv bootsupport /media/card/bootsupport
        echo -n "updated "

        # golden files are not overwritten in the field
        if [ ! -d /mnt/kernel/bootsupport ]
        then
            cp -r /media/card/bootsupport /mnt/kernel/bootsupport
            echo "and installed golden copy"
        fi
    else
        echo "skipped (not applicable)"
    fi

    rmdir /mnt/kernel/.tmp
    umount /mnt/kernel
    rmdir /mnt/kernel
    return $RET
}

update_WiFi() {
    grep -q /media/card /proc/mounts || mount /media/card
    mkdir -p /media/card/WiFi

    echo -n "WiFi ... "

    if [ -d WiFi ]
    then
        # mv can't overwrite an existing tree
        rm -r /media/card/WiFi
        mv WiFi /media/card/WiFi
        echo updated
        return 0
    else
        echo not found
        return 1
    fi
}

update_firmware() {
    grep -q /media/card /proc/mounts || mount /media/card

    echo -n "firmware ... "

    if test -d firmware
    then
        mkdir -p /media/card/firmware
        rm -r /media/card/firmware
        mv firmware /media/card/firmware
        echo updated
        return 0
    else
        echo not found
        return 1
    fi
}

update_bbuwaveforms(){
    grep -q /media/card /proc/mounts || mount /media/card
   
    echo -n "bbu waveforms ..."
    
    if test -d bbuwaveforms
    then
        mkdir -p /media/card/bbuwaveforms
        rm -rf /media/card/bbuwaveforms
        mv bbuwaveforms /media/card/bbuwaveforms
        
	mkdir -p /mnt/usrroot/bbuwaveforms/internal/

	for FREQ in 5MHz 10MHz 15MHz 20MHz
    	do
	   if ! test -d  /media/card/bbuwaveforms/$FREQ
	   then
    		echo "/media/card/bbuwaveforms/$FREQ does not exist:"
    	   	return 1
	   fi
           
           mkdir -p /mnt/usrroot/bbuwaveforms/$FREQ
	   ln -sfn /media/card/bbuwaveforms/$FREQ /mnt/usrroot/bbuwaveforms/internal/$FREQ
    	done   
	
	echo updated     
	return 0
    else
        echo "skipped (not applicable)"
        return 0
    fi
}

update_bbuscripts(){
    grep -q /media/card /proc/mounts || mount /media/card
    echo -n "bbu scripts ..."
    
    if test -d bbuscripts
    then
        mkdir -p /media/card/bbuscripts
        rm -rf /media/card/bbuscripts
        mv bbuscripts /media/card/bbuscripts

	if ! test -d /media/card/bbuscripts/genericscripts
	then
	  echo "package missing generic scripts"
	  return 1
	fi

	if ! test -d /media/card/bbuscripts/aluscripts
	then
	  echo "package missing alu specific scripts"
	  return 1
	fi

	mkdir -p /mnt/usrroot/bbuscripts/internal/
	ln -sfn /media/card/bbuscripts/aluscripts /mnt/usrroot/bbuscripts/internal/aluscripts

	echo updated
        return 0
    else
        echo "skipped (not applicable)"
        return 0
    fi
}

update_versionnum() {
    mkdir -p /media/card/version
    rm -f /media/card/version/firmware &> /dev/null
    echo "$PACKAGE_ID" > /media/card/version/firmware
    chmod a-w /media/card/version/firmware
    echo "Firmware version updated to '$PACKAGE_ID'"
}

set_ownership() {
    # Everything we have installed should be owned by root. For example,
    # /media/card/fpga is owned by 107:115 which is not a valid user on this
    # system.
    chown -R root:root /media/card
    chown -R root:root /mnt/systemroot

    # make sure system identity and version are readable by all users
    mkdir -p /media/card/version
    chmod a+x /media/card/version
    find /media/card/version -type d -exec chmod o+rx '{}' \;
    find /media/card/version -type f -exec chmod o+r '{}' \;

    mkdir -p /media/card/identity
    chmod a+x /media/card/identity

    find /media/card/identity -type d -exec chmod a+rx '{}' \;
    find /media/card/identity -type f -exec chmod a+r '{}' \;
}

clear_last_used_settings() {
    rm -rf /etc/anritsu/settings
}

prepare_swap() {
    # This procedure only needs to be done once
    SWPFILE=/mnt/systemroot/swapfile
    if ! test -f $SWPFILE
    then
        dd if=/dev/zero of=$SWPFILE bs=1M count=150
        chmod 600 $SWPFILE
        mkswap $SWPFILE
    fi
}

fw_setenv fwupdatestatus "installing_firmware"

update_cpu1
update_fpga
update_framework
update_versionnum
update_config
clear_last_used_settings
update_kernel
update_WiFi
update_firmware
update_bbuwaveforms
update_bbuscripts
prepare_swap
set_ownership
fw_setenv fwupdatestatus "not_in_progress"
fw_setenv shboottype normal
fw_setenv bootfile image.ub

sync

/sbin/reboot

