#!/bin/sh -e

### SYSTEM DEFINITIONS ########################################################
BLK_NAME_A="mtdblock2"
BLK_NAME_B="mtdblock3"
KERNEL_UIMAGE="/opt/fwu/system_offline/boot/uImage"
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"
    printf "Application files are SET to be updated.\\n"
  else
    printf "application files are NOT set to be updated.\\n"
  fi

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

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

check_executable()
{
  [ -x "$1" ] || (printf "FATAL: %s binary is not executable.\\n" "$1" && exit 1)
}

check_file()
{
  [ -f "$1" ] || (printf "FATAL: %s is not a file.\\n" "$1" && exit 2)
}

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

identify_offline()
{
  printf "Identifying offline system...\\n"
  MTD_CURRENT=`cat /proc/cmdline | cut -d " " -f 3 | cut -d "/" -f 3`
  printf "MTD_CURRENT=%s\\n" "${MTD_CURRENT}"
  
  if [ "${COMMISSION}" = "FALSE" ]
  then
    BOOTED_SYSTEM=$(${DEVICESTACK} --booted-system)
    printf "BOOTED_SYSTEM=%s\\n" "${BOOTED_SYSTEM}"
    if [ "${BOOTED_SYSTEM}" = "A" ]
    then
      printf "Update Mode -> Determined System B as offline system.\\n"
      set_offline_system_b
    elif [ "${BOOTED_SYSTEM}" = "B" ]
    then
      printf "Update Mode -> Determined System A as offline system.\\n"
      set_offline_system_a
    else
      printf "FATAL: Failed to identify booted system. BOOTED_SYSTEM=%s\\n" "${BOOTED_SYSTEM}"
      exit 9
    fi
  elif  [ "${COMMISSION}" = "TRUE" ]
  then
    printf "Commissioning Mode -> Selecting System B as offline system.\\n"
    set_offline_system_b
  else
    printf "FATAL: Failed to identify whether running in Commissioning mode or not!. COMMISSION=%s\\n" "${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
#    printf "Testing bad blocks for offline kernel (%s)...\\n" "${OFFLINE_KERNEL_MTD}"
#    "${NAND_TEST}" --markbad "${OFFLINE_KERNEL_MTD}"
#    printf "Testing bad blocks for offline system (%s)\\n" "${OFFLINE_SYSTEM_MTD}"
#    "${NAND_TEST}" --markbad "${OFFLINE_SYSTEM_MTD}"
#  fi
#
##

  if [ -x "${FLASH_ERASEALL}" ]
  then
    printf "Erasing offline kernel (%s %s)...\\n" "${FLASH_ERASEALL}" "${OFFLINE_KERNEL_MTD}"
    "${FLASH_ERASEALL}" --quiet "${OFFLINE_KERNEL_MTD}"
    printf "Erasing offline system (%s %s)...\\n" "${FLASH_ERASEALL}" "${OFFLINE_SYSTEM_MTD}"
    "${FLASH_ERASEALL}" --quiet --jffs2 "${OFFLINE_SYSTEM_MTD}"
  else
    if [ -x "${FLASH_ERASE}" ]
    then
      printf "Erasing offline kernel (flash_erase %s 0 0)...\\n" "${OFFLINE_KERNEL_MTD}"
      "${FLASH_ERASE}" --quiet "${OFFLINE_KERNEL_MTD}" 0 0
      printf "Erasing offline system (flash_erase %s 0 0)...\\n" "${OFFLINE_SYSTEM_MTD}"
      "${FLASH_ERASE}" --quiet --jffs2 "${OFFLINE_SYSTEM_MTD}" 0 0
    else
      printf "FATAL: No suitable flash erase tool found.\\n"
      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
#    printf "Testing bad blocks for user data (%s)...\\n" "${USER_DATA_MTD}"
#    "${NAND_TEST}" --markbad "${USER_DATA_MTD}"
#  fi
#
##

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

mount_offline_system()
{
  printf "Mounting offline system (%s)...\\n" "${OFFLINE_SYSTEM_BLK}"
  mkdir -p "${OFFLINE_SYSTEM_DIR}"
  "${MOUNT}" -t jffs2 "${OFFLINE_SYSTEM_BLK}" "${OFFLINE_SYSTEM_DIR}"
}

unmount_offline_system()
{
  printf "Unmounting offline system (%s)...\\n" "${OFFLINE_SYSTEM_DIR}"
  ("${UNMOUNT}" "${OFFLINE_SYSTEM_DIR}") || printf "Failed to unmount offline system. May be already unmounted.\\n"
}

mount_offline_kernel()
{
  printf "Mounting offline kernel (%s)...\\n" "${OFFLINE_KERNEL_BLK}"
  mkdir -p "${OFFLINE_KERNEL_DIR}"
  "${MOUNT}" -t jffs2 "${OFFLINE_KERNEL_BLK}" "${OFFLINE_KERNEL_DIR}"
}

unmount_offline_kernel()
{
  printf "Unmounting offline kernel (%s)...\\n" "${OFFLINE_KERNEL_DIR}"
  ("${UNMOUNT}" "${OFFLINE_KERNEL_DIR}") || printf "Failed to unmount offline kernel. May be already unmounted.\\n"
}

mount_current_system()
{
  printf "Mounting current system (%s)...\\n" "${CURRENT_SYSTEM_BLK}"
  mkdir -p "${CURRENT_SYSTEM_DIR}"
  "${MOUNT}" -t jffs2 -o ro "${CURRENT_SYSTEM_BLK}" "${CURRENT_SYSTEM_DIR}"
}

unmount_current_system()
{
  printf "Unmounting current system (%s)...\\n" "${CURRENT_SYSTEM_DIR}"
  ("${UNMOUNT}" "${CURRENT_SYSTEM_DIR}") || printf "Failed to unmount current system. May be already unmounted.\\n"
}

mount_current_kernel()
{
  printf "Mounting current kernel (%s)...\n" "${CURRENT_KERNEL_BLK}"
  mkdir -p "${CURRENT_KERNEL_DIR}"
  "${MOUNT}" -t jffs2 -o ro "${CURRENT_KERNEL_BLK}" "${CURRENT_KERNEL_DIR}"
}

unmount_current_kernel()
{
  printf "Unmounting current kernel (%s)...\\n" "${CURRENT_KERNEL_DIR}"
  ("${UNMOUNT}" "${CURRENT_KERNEL_DIR}") || printf "Failed to unmount current kernel. May be already unmounted.\\n"
}

clone_current_system()
{
  printf "Cloning current system (%s -> %s)...\\n" "${CURRENT_SYSTEM_DIR}" "${OFFLINE_SYSTEM_DIR}"
  (cd "${CURRENT_SYSTEM_DIR}" && tar cf - . | (cd "${OFFLINE_SYSTEM_DIR}" && tar xf -))
}

clone_current_kernel()
{
  printf "Writing first kernel to %s at %s...\\n" "${OFFLINE_KERNEL_MTD}" "${FIRST_KERNEL_OFFSET}"
  nandwrite --markbad --pad --start="${FIRST_KERNEL_OFFSET}" "${OFFLINE_KERNEL_MTD}" "${KERNEL_UIMAGE}"
  printf "Writing second kernel to %s at %s...\\n" "${OFFLINE_KERNEL_MTD}" "${SECOND_KERNEL_OFFSET}"
  nandwrite --markbad --pad --start="${FIRST_KERNEL_OFFSET}" "${OFFLINE_KERNEL_MTD}" "${KERNEL_UIMAGE}"
}

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

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

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

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

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

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

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

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

# usage: log TAG MESSAGE
#   TAG       The tag for the message
#   MESSAGE   The message
# Logs a message to the system log and std::out as user.notice.
log()
{
  if [ -f /usr/bin/logger ]
  then
    /usr/bin/logger -s -t "$1" "$2"
  else
    echo "$1 $2"
  fi
}

# usage: log_error TAG MESSAGE
#   TAG       The tag for the message
#   MESSAGE   The message
# Logs a message to the system log and std::out as user.error.
log_error()
{
  if [ -f /usr/bin/logger ]
  then
    /usr/bin/logger -p user.error -s -t "$1" "$2"
  else
    echo "$1 $2"
  fi
}
