#!/bin/sh -e

### SYSTEM DEFINITIONS ########################################################
BLK_NAME_A="mtdblock2"
BLK_NAME_B="mtdblock3"
KERNEL_UZIMAGE="/opt/fwu/system_offline/boot/uzImage"
BOOTED_SYSTEM="?"

### BINARIES ##################################################################
SH="/bin/sh"
MOUNT="/bin/mount"
UNMOUNT="/bin/umount"
FLASH_ERASE="/usr/sbin/flash_erase"
FLASH_ERASEALL="/usr/sbin/flash_eraseall"
NAND_TEST="/usr/sbin/nandtest"
CHMOD="/bin/chmod"
CHOWN="/bin/chown"
DEVICESTACK="/opt/gira/bin/devicestack"
DEVICECONFIG="/opt/userdata/devicestack/devicestackconfig.xml"
[ -x ${DEVICESTACK} ] || DEVICESTACK="/opt/im/DeviceStack"
BOOT_UPDATER="/opt/gira/bin/boot_updater"
[ -e ${BOOT_UPDATER} ] || BOOT_UPDATER="/opt/fwu/BootUpdaterLegacy"

### STATUS FILES ##############################################################
CLONE_STATUS_FILE="/opt/userdata/.clone-status"
FWU_STATUS_FILE="/opt/userdata/.fwu-status"
SSH_ENABLE_FILE="/opt/userdata/.ssh-enabled"
WATCHDOG_ENABLE_FILE="/opt/userdata/.wd-enabled"

### LOG FILES #################################################################
CLONE_LOG="/var/log/clone.log"
FWU_PRE_LOG="/var/log/fwu_pre.log"
FWU_POST_LOG="/var/log/fwu_post.log"

### INIT SCRIPTS ##############################################################
INITD_SSH="/etc/init.d/S50dropbear"
INITD_WATCHDOG="/etc/init.d/S15watchdog"

### MOUNT POINTS ##############################################################
OFFLINE_KERNEL_DIR="/opt/fwu/kernel_offline"
OFFLINE_SYSTEM_DIR="/opt/fwu/system_offline"
CURRENT_KERNEL_DIR="/opt/fwu/kernel_current"
CURRENT_SYSTEM_DIR="/opt/fwu/system_current"

### KERNEL OFFSETS ############################################################
FIRST_KERNEL_OFFSET="0x0"
SECOND_KERNEL_OFFSET="0x400000"
SYSTEM_A_FIRST_KERNEL_OFFSET="0x0"
SYSTEM_A_SECOND_KERNEL_OFFSET="0x400000"
SYSTEM_B_FIRST_KERNEL_OFFSET="0x0"
SYSTEM_B_SECOND_KERNEL_OFFSET="0x400000"

### SYSTEM A DEVICES ##########################################################
MTD_KERNEL_A="/dev/mtd0"
MTD_SYSTEM_A="/dev/mtd2"
BLK_KERNEL_A="/dev/mtdblock0"
BLK_SYSTEM_A="/dev/mtdblock2"

### SYSTEM B DEVICES ##########################################################
MTD_KERNEL_B="/dev/mtd1"
MTD_SYSTEM_B="/dev/mtd3"
BLK_KERNEL_B="/dev/mtdblock1"
BLK_SYSTEM_B="/dev/mtdblock3"

### ADDITIONAL DEVICES ########################################################
USER_DATA_MTD="/dev/mtd4"
USER_DATA_BLK="/dev/mtdblock4"

### SCRIPT PARAMETERS #########################################################
APP_UPDATE="FALSE"
SYS_UPDATE="FALSE"
COMMISSION="FALSE"

### FUNCTIONS #################################################################
parse_arguments()
{
  if [ "$1" = "/UpdateApplicationFiles:True" ]
  then
    APP_UPDATE="TRUE"
    echo "Application files are SET to be updated."
  else
    echo "application files are NOT set to be updated."
  fi

  if [ "$2" = "/UpdateSystemFiles:True" ]
  then
    SYS_UPDATE="TRUE"
    echo "System files are SET to be updated.."
  else
    echo "System files are NOT set to be updated.."
  fi

  if [ "$3" = "/CommissioningMode:True" ]
  then
    COMMISSION="TRUE"
    echo "Running in commissioning mode."
  else
    echo "Running in update mode."
  fi
}

check_executable()
{
  [ -x $1 ] || (echo "FATAL: $1 binary is not executable." && exit 1)
}

check_file()
{
  [ -f $1 ] || (echo "FATAL: $1 is not a file." && exit 2)
}

sync_buffer()
{
  echo "Free pagecache, dentries and inodes..."
  echo 3 > /proc/sys/vm/drop_caches
  echo "Sync..."
  sync
  sleep 1
  sync
  sleep 1
  sync
  sleep 1
}

identify_offline()
{
  echo "Identifying offline system..."
  # We naively assume the root from the cmline is the current root device.
  # Use with caution! Breaks when using initrd for example.
  MTD_CURRENT=`cat /proc/cmdline | sed -e 's/^.*root=//' -e 's/ .*$//'`
  echo "MTD_CURRENT=${MTD_CURRENT}"
  
  if [ "${COMMISSION}" = "FALSE" ]
  then
    BOOTED_SYSTEM=$(${DEVICESTACK} --booted-system)
    echo "BOOTED_SYSTEM=${BOOTED_SYSTEM}"
    if [ "${BOOTED_SYSTEM}" = "A" ]
    then
      echo "Update Mode -> Determined System B as offline system."
      set_offline_system_b
    elif [ "${BOOTED_SYSTEM}" = "B" ]
    then
      echo "Update Mode -> Determined System A as offline system."
      set_offline_system_a
    else
      echo "FATAL: Failed to identify booted system. BOOTED_SYSTEM=${BOOTED_SYSTEM}"
      exit 9
    fi
  elif  [ "${COMMISSION}" = "TRUE" ]
  then
    echo "Commissioning Mode -> Selecting System B as offline system."
    set_offline_system_b
  else
    echo "FATAL: Failed to identify whether running in Commissioning mode or not!. COMMISSION=${COMMISSION}"
    exit 9
  fi
}

set_offline_system_a()
{
  FIRST_KERNEL_OFFSET=${SYSTEM_A_FIRST_KERNEL_OFFSET}
  SECOND_KERNEL_OFFSET=${SYSTEM_A_SECOND_KERNEL_OFFSET}
  OFFLINE_KERNEL_MTD=${MTD_KERNEL_A}
  OFFLINE_KERNEL_BLK=${BLK_KERNEL_A}
  OFFLINE_SYSTEM_MTD=${MTD_SYSTEM_A}
  OFFLINE_SYSTEM_BLK=${BLK_SYSTEM_A}
  CURRENT_KERNEL_MTD=${MTD_KERNEL_B}
  CURRENT_KERNEL_BLK=${BLK_KERNEL_B}
  CURRENT_SYSTEM_MTD=${MTD_SYSTEM_B}
  CURRENT_SYSTEM_BLK=${BLK_SYSTEM_B}
}

set_offline_system_b()
{
  FIRST_KERNEL_OFFSET=${SYSTEM_B_FIRST_KERNEL_OFFSET}
  SECOND_KERNEL_OFFSET=${SYSTEM_B_SECOND_KERNEL_OFFSET}
  OFFLINE_KERNEL_MTD=${MTD_KERNEL_B}
  OFFLINE_KERNEL_BLK=${BLK_KERNEL_B}
  OFFLINE_SYSTEM_MTD=${MTD_SYSTEM_B}
  OFFLINE_SYSTEM_BLK=${BLK_SYSTEM_B}
  CURRENT_KERNEL_MTD=${MTD_KERNEL_A}
  CURRENT_KERNEL_BLK=${BLK_KERNEL_A}
  CURRENT_SYSTEM_MTD=${MTD_SYSTEM_A}
  CURRENT_SYSTEM_BLK=${BLK_SYSTEM_A}
}

erase_offline()
{
## Before enabling this part, please ensure that time out in update process is
## big enough to cover this test and has no other impact.
#
#  if [ -x ${NAND_TEST} ]
#  then
#    echo "Testing bad blocks for offline kernel (${OFFLINE_KERNEL_MTD})..."
#    ${NAND_TEST} --markbad ${OFFLINE_KERNEL_MTD}
#    echo "Testing bad blocks for offline system (${OFFLINE_SYSTEM_MTD})..."
#    ${NAND_TEST} --markbad ${OFFLINE_SYSTEM_MTD}
#  fi
#
##

  if [ -x ${FLASH_ERASEALL} ]
  then
    echo "Erasing offline kernel (${FLASH_ERASEALL} ${OFFLINE_KERNEL_MTD})..."
    ${FLASH_ERASEALL} --quiet ${OFFLINE_KERNEL_MTD}
    echo "Erasing offline system (${FLASH_ERASEALL} ${OFFLINE_SYSTEM_MTD})..."
    ${FLASH_ERASEALL} --quiet --jffs2 ${OFFLINE_SYSTEM_MTD}
  else
    if [ -x ${FLASH_ERASE} ]
    then
      echo "Erasing offline kernel (flash_erase ${OFFLINE_KERNEL_MTD} 0 0)..."
      ${FLASH_ERASE} --quiet ${OFFLINE_KERNEL_MTD} 0 0
      echo "Erasing offline system (flash_erase ${OFFLINE_SYSTEM_MTD} 0 0)..."
      ${FLASH_ERASE} --quiet --jffs2 ${OFFLINE_SYSTEM_MTD} 0 0
    else
      echo "FATAL: No suitable flash erase tool found."
      exit 7
    fi
  fi
}

erase_userdata()
{
## Before enabling this part, please ensure that time out in update process is
## big enough to cover this test and has no other impact.
#
#  if [ -x ${NAND_TEST} ]
#  then
#    echo "Testing bad blocks for user data (${USER_DATA_MTD})..."
#    ${NAND_TEST} --markbad ${USER_DATA_MTD}
#  fi
#
##

  if [ -x ${FLASH_ERASEALL} ]
  then
    echo "Erasing user data (${FLASH_ERASEALL} ${USER_DATA_MTD})..."
    ${FLASH_ERASEALL} --quiet --jffs2 ${USER_DATA_MTD}
  else
    if [ -x ${FLASH_ERASE} ]
    then
      echo "Erasing user data (flash_erase ${USER_DATA_MTD} 0 0)..."
      ${FLASH_ERASE} --quiet --jffs2 ${USER_DATA_MTD} 0 0
    else
      echo "No suitable flash erase tool found."
      exit 8
    fi
  fi
}

mount_offline_system()
{
  echo "Mounting offline system (${OFFLINE_SYSTEM_BLK})..."
  mkdir -p ${OFFLINE_SYSTEM_DIR}
  ${MOUNT} -t jffs2 ${OFFLINE_SYSTEM_BLK} ${OFFLINE_SYSTEM_DIR}
}

unmount_offline_system()
{
  echo "Unmounting offline system (${OFFLINE_SYSTEM_DIR})..."
  (${UNMOUNT} ${OFFLINE_SYSTEM_DIR}) || echo "Failed to unmount offline system. May be already unmounted."
}

mount_offline_kernel()
{
  echo "Mounting offline kernel (${OFFLINE_KERNEL_BLK})..."
  mkdir -p ${OFFLINE_KERNEL_DIR}
  ${MOUNT} -t jffs2 ${OFFLINE_KERNEL_BLK} ${OFFLINE_KERNEL_DIR}
}

unmount_offline_kernel()
{
  echo "Unmounting offline kernel (${OFFLINE_KERNEL_DIR})..."
  (${UNMOUNT} ${OFFLINE_KERNEL_DIR}) || echo "Failed to unmount offline kernel. May be already unmounted."
}

mount_current_system()
{
  echo "Mounting current system (${CURRENT_SYSTEM_BLK})..."
  mkdir -p ${CURRENT_SYSTEM_DIR}
  ${MOUNT} -t jffs2 -o ro ${CURRENT_SYSTEM_BLK} ${CURRENT_SYSTEM_DIR}
}

unmount_current_system()
{
  echo "Unmounting current system (${CURRENT_SYSTEM_DIR})..."
  (${UNMOUNT} ${CURRENT_SYSTEM_DIR}) || echo "Failed to unmount current system. May be already unmounted."
}

mount_current_kernel()
{
  echo "Mounting current kernel (${CURRENT_KERNEL_BLK})..."
  mkdir -p ${CURRENT_KERNEL_DIR}
  ${MOUNT} -t jffs2 -o ro ${CURRENT_KERNEL_BLK} ${CURRENT_KERNEL_DIR}
}

unmount_current_kernel()
{
  echo "Unmounting current kernel (${CURRENT_KERNEL_DIR})..."
  (${UNMOUNT} ${CURRENT_KERNEL_DIR}) || echo "Failed to unmount current kernel. May be already unmounted."
}

clone_current_system()
{
  echo "Cloning current system (${CURRENT_SYSTEM_DIR} -> ${OFFLINE_SYSTEM_DIR})..."
  (cd ${CURRENT_SYSTEM_DIR} && tar cf - . | (cd ${OFFLINE_SYSTEM_DIR} && tar xf -))
}

clone_current_kernel()
{
  echo "Writing first kernel to ${OFFLINE_KERNEL_MTD} at ${FIRST_KERNEL_OFFSET}..."
  nandwrite --markbad --pad --start=${FIRST_KERNEL_OFFSET} ${OFFLINE_KERNEL_MTD} ${KERNEL_UZIMAGE}
  echo "Writing second kernel to ${OFFLINE_KERNEL_MTD} at ${SECOND_KERNEL_OFFSET}..."
  nandwrite --markbad --pad --start=${FIRST_KERNEL_OFFSET} ${OFFLINE_KERNEL_MTD} ${KERNEL_UZIMAGE}
}

remount_current_system_rw()
{
  echo "Remounting current system writable..."
  (${MOUNT} -o remount,rw /) || echo "Failed to remount current system as writable."
}

remount_current_system_ro()
{
  echo "Remounting current system read-only..."
  sync
  (${MOUNT} -o remount,ro /) || echo "Failed to remount current system as read-only."
}

umount_single()
{
  echo -n "Unmounting $1 ... "
  if [ "$(mount | grep $1 | wc -l)" -gt "0" ]
  then
    umount $1
    echo "done."
  else
    echo "not mounted."
  fi
}

umount_all()
{
  umount_single /opt/userdata/fwu/system_offline
  umount_single /opt/userdata/fwu/system_current
}

disable_ssh()
{
  if [ "${COMMISSION}" != "TRUE" ]
  then
    echo "Disable SSH on every update..."
    if [ -x ${INITD_SSH} ]
    then
      rm -f ${SSH_ENABLE_FILE}
    fi
  fi
}

enable_ssh()
{
  if [ "${COMMISSION}" != "TRUE" ]
  then
    echo "Enable SSH on every update..."
    if [ -x ${INITD_SSH} ]
    then
      touch ${SSH_ENABLE_FILE}
    fi
  fi
}

disable_watchdog_trigger()
{
  if [ "${COMMISSION}" != "TRUE" ]
  then
    echo "Disable watchdog trigger on every update..."
    if [ -x ${INITD_WATCHDOG} ]
    then
      rm -f ${WATCHDOG_ENABLE_FILE}
    fi
  fi
}

enable_watchdog_trigger()
{
  if [ "${COMMISSION}" != "TRUE" ]
  then
    echo "Enable watchog trigger on every update..."
    if [ -x ${INITD_WATCHDOG} ]
    then
      touch ${WATCHDOG_ENABLE_FILE}
    fi
  fi
}

