#!/bin/sh

################################################################################
#      This file is part of OpenELEC - http://www.openelec.tv
#      Copyright (C) 2009-2014 Stephan Raue (stephan@openelec.tv)
#      Copyright (C) 2010-2011 Roman Weber (roman@openelec.tv)
#      Copyright (C) 2012 Yann Cézard (eesprit@free.fr)
#
#  OpenELEC is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 2 of the License, or
#  (at your option) any later version.
#
#  OpenELEC is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with OpenELEC.  If not, see <http://www.gnu.org/licenses/>.
################################################################################

  # create directories
  /bin/busybox mkdir -p /dev
  /bin/busybox mkdir -p /proc
  /bin/busybox mkdir -p /sys
  /bin/busybox mkdir -p /tmp
  /bin/busybox mkdir -p /flash
  /bin/busybox mkdir -p /sysroot
  /bin/busybox mkdir -p /storage

  # mount all needed special filesystems
  /bin/busybox mount -t devtmpfs devtmpfs /dev
  /bin/busybox mount -t proc proc /proc
  /bin/busybox mount -t sysfs sysfs /sys

  # common functions
  . /functions

  # set needed variables
  MODULE_DIR=/lib/modules

  UPDATE_ROOT=/storage/.update
  UPDATE_DIR="$UPDATE_ROOT"

  UPDATE_KERNEL="KERNEL"
  UPDATE_SYSTEM="SYSTEM"
  IMAGE_KERNEL="KERNEL"
  IMAGE_SYSTEM="SYSTEM"

  BOOT_STEP="start"
  MD5_FAILED="0"
  RUN_FSCK="yes"
  RUN_FSCK_DISKS=""

  NBD_DEVS="0"
  FLASH_FREE_MIN="5"

  INSTALLED_MEMORY=`cat /proc/meminfo | grep 'MemTotal:' | awk '{print $2}'`
  SYSTEM_TORAM_LIMIT=1024000

  LIVE="no"

  # hide kernel log messages on console
  echo '1 4 1 7' > /proc/sys/kernel/printk

  # set ondemand up_threshold
  if [ -e /sys/devices/system/cpu/cpufreq/ondemand/up_threshold ] ; then
    echo 50 > /sys/devices/system/cpu/cpufreq/ondemand/up_threshold
  fi

  # run platform_init script if exists
  if [ -f "./platform_init" ]; then
    ./platform_init
  fi

  # clear screen and hide cursor
  clear
  hidecursor

  # parse command line arguments
  for arg in $(cat /proc/cmdline); do
    case $arg in
      BOOT_IMAGE=*)
        IMAGE_KERNEL="${arg#*=}"
        ;;
      SYSTEM_IMAGE=*)
        IMAGE_SYSTEM="${arg#*=}"
        ;;
      boot=*)
        boot="${arg#*=}"
        case $boot in
          ISCSI=*|NBD=*|NFS=*)
            UPDATE_DISABLED=yes
            FLASH_NETBOOT=yes
            ;;
          /dev/*|LABEL=*|UUID=*)
            RUN_FSCK_DISKS="$RUN_FSCK_DISKS $boot"
            ;;
        esac
        ;;
      disk=*)
        disk="${arg#*=}"
        case $disk in
          ISCSI=*|NBD=*|NFS=*)
            STORAGE_NETBOOT=yes
            ;;
          /dev/*|LABEL=*|UUID=*)
            RUN_FSCK_DISKS="$RUN_FSCK_DISKS $disk"
            ;;
        esac
        ;;
      wol_mac=*)
        wol_mac="${arg#*=}"
        ;;
      wol_wait=*)
        wol_wait="${arg#*=}"
        ;;
      textmode)
        INIT_UNIT="--unit=textmode.target"
        ;;
      installer)
        INIT_UNIT="--unit=installer.target"
        ;;
      debugging)
        DEBUG=yes
        ;;
      progress)
        PROGRESS=yes
        INIT_ARGS="$INIT_ARGS --show-status=1"
        ;;
      nofsck)
        RUN_FSCK=no
        ;;
      nosplash)
        SPLASH=no
        ;;
      noram)
        SYSTEM_TORAM=no
        ;;
      live)
        LIVE=yes
        ;;
      overlay)
        OVERLAY=yes
        ;;
      setfbres=*)
        SWITCH_FRAMEBUFFER="${arg#*=}"
        SWITCH_FRAMEBUFFER="${SWITCH_FRAMEBUFFER//,/ }"
        ;;
      break=*)
        BREAK="${arg#*=}"
        ;;
    esac
  done

  if test "$DEBUG" = "yes"; then
    exec 3>&1
  else
    exec 3>/dev/null
  fi
  SILENT_OUT=3

  progress() {
    if test "$PROGRESS" = "yes"; then
      echo "### $1 ###"
    fi
  }

  debug_msg() {
    echo "$1" >&$SILENT_OUT
  }

  debug_shell() {
    echo "### Starting debugging shell... type  exit  to quit ###"

    showcursor

    sh </dev/tty1 >/dev/tty1 2>&1
  }

  error() {
    # Display fatal error message
    # $1:action which caused error, $2:message
    echo "*** Error in $BOOT_STEP: $1: $2 ***"
    debug_shell
  }

  break_after() {
    # Start debug shell after boot step $1
    case $BREAK in
      all|*$1*)
        debug_shell
        ;;
    esac
  }

  # Mount handlers
  # All handlers take the following parameters:
  # $1:target, $2:mountpoint, $3:mount options, [$4:fs type]

  mount_common() {
    # Common mount handler, handles block devices and filesystem images
    MOUNT_OPTIONS="-o $3"
    [ -n "$4" ] && MOUNT_OPTIONS="-t $4 $MOUNT_OPTIONS"

    for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
      ERR_ENV=1

      mount $MOUNT_OPTIONS $1 $2 >&$SILENT_OUT 2>&1
      [ "$?" -eq "0" ] && ERR_ENV=0 && break

      usleep 1000000
    done
    [ "$ERR_ENV" -ne "0" ] && error "mount_common" "Could not mount $1"
  }

  get_iscsistart_options() {
    # Convert kernel commandline ISCSI= options to iscsistart options
    IFS_SAVE="$IFS"
    IFS=,

    for arg in $1; do
      val="${arg#*=}"
      case "$arg" in
        iscsi_initiator=*)
          option="-i"
          ;;
        iscsi_target_name=*)
          option="-t"
          ;;
        iscsi_target_ip=*)
          option="-a"
          ;;
        iscsi_target_port=*)
          option="-p"
          ;;
        iscsi_target_group=*)
          option="-g"
          ;;
        iscsi_username=*)
          option="-u"
          ;;
        iscsi_password=*)
          option="-w"
          ;;
        iscsi_in_username=*)
          option="-U"
          ;;
        iscsi_in_password=*)
          option="-W"
          ;;
      esac
      echo "$option $val"
    done

    IFS="$IFS_SAVE"
  }

  mount_iscsi() {
    # Mount iSCSI target
    ISCSI_DEV="${1##*,}"
    ISCSI_OPTIONS="${1%,*}"

    if [ ! -f "/sbin/iscsistart" ]; then
      error "iscsistart" "iSCSI support not available"
    fi

    if [ "$ISCSI_OPTIONS" = "auto" ]; then
      progress "Network configuration based on iBFT"
      /sbin/iscsistart -N >&$SILENT_OUT 2>&1 || \
        error "iscsistart" "Unable to configure network"
      progress "iSCSI auto connect based on iBFT"
      /sbin/iscsistart -b >&$SILENT_OUT 2>&1 || \
        error "iscsistart" "Unable to auto connect"
    else
      /sbin/iscsistart $(get_iscsistart_options "$ISCSI_OPTIONS") >&$SILENT_OUT 2>&1 || \
        error "iscsistart" "Unable to connect to ISCSI target"
    fi

    mount_common "$ISCSI_DEV" "$2" "$3" "$4"
  }

  mount_nbd() {
  # Mount NBD device
    NBD_SERVER="${1%%:*}"
    NBD_PORT="${1#*:}"
    NBD_DEV="/dev/nbd$NBD_DEVS"

    nbd-client $NBD_SERVER $NBD_PORT $NBD_DEV >&$SILENT_OUT 2>&1 || \
      error "nbd-client" "Could not connect to NBD server $1"

    mount_common "$NBD_DEV" "$2" "$3" "$4"

    NBD_DEVS=$(( ${NBD_DEVS} + 1 ))
  }

  mount_nfs() {
    # Mount NFS export
    NFS_EXPORT="${1%%,*}"
    NFS_OPTIONS="${1#*,}"

    [ "$NFS_OPTIONS" = "$1" ] && NFS_OPTIONS=

    mount_common "$NFS_EXPORT" "$2" "$3,nolock,soft,timeo=3,retrans=2,rsize=32768,wsize=32768,$NFS_OPTIONS" "nfs"
  }

  mount_ubifs() {
    mount_common "$1" "$2" "$3" "ubifs"
  }

  mount_part() {
    # Mount a local or network filesystem
    # $1:[TYPE=]target, $2:mountpoint, $3:mount options, [$4:fs type]
    progress "mount filesystem $1 ..."

    MOUNT_TARGET="${1#*=}"
    case $1 in
      /dev/ubi*)
        MOUNT_CMD="mount_ubifs"
        MOUNT_TARGET="$1"
        RUN_FSCK="no"
        ;;
      LABEL=*|UUID=*|/*)
        MOUNT_CMD="mount_common"
        MOUNT_TARGET="$1"
        ;;
      ISCSI=*)
        MOUNT_CMD="mount_iscsi"
        ;;
      NBD=*)
        MOUNT_CMD="mount_nbd"
        ;;
      NFS=*)
        MOUNT_CMD="mount_nfs"
        ;;
      *)
        error "mount_part" "Unknown filesystem $1"
        ;;
    esac
    $MOUNT_CMD "$MOUNT_TARGET" "$2" "$3" "$4"
  }

  update_file() {
    if [ -f "$UPDATE_DIR/$2" -a -f "$3" ]; then
      mount -o remount,rw /flash

      StartProgress percent "Updating $1... " "$3" $(stat -t "$UPDATE_DIR/$2" | awk '{print $2}')
        # use dd here with conv=fsync so that all writes are non-buffered
        # ensuring accurate progress - take the sync hit during the
        # transfer, rather than when flushing file buffers after the progress
        # meter declares the transfer already complete
        dd if=$UPDATE_DIR/$2 of=$3 bs=1M conv=fsync 2>/dev/null
        StopProgress

      # loopback file needs writable /flash all the time
      if [ "${disk%%=*}" != "FILE" ]; then
        mount -o remount,ro /flash
      fi
      sync
    fi
  }

  update_partition() {
    local result

    if [ -f "$UPDATE_DIR/$2" -a -b "$3" ]; then
      StartProgress spinner "Updating $1... "
        result="$(dd if="$UPDATE_DIR/$2" of="$3" conv=fsync 2>&1)"
        StopProgress "done"
      echo "${result}"
    fi
   }

  update_bootloader() {
    local result

    export BOOT_ROOT="/flash"
    export SYSTEM_ROOT="/sysroot"

    mount_part "/flash/$IMAGE_SYSTEM" "/sysroot" "ro,loop"

    if [ -f $SYSTEM_ROOT/usr/share/bootloader/update.sh ]; then
      StartProgress spinner "Updating Bootloader... "
        result="$(sh $SYSTEM_ROOT/usr/share/bootloader/update.sh 2>&1)"
        sync
        StopProgress "done"
      [ -n "${result}" ] && echo "${result}"
    fi

    umount /sysroot
  }

  load_modules() {
    progress "Loading kernel modules"

    [ ! -f "/etc/modules" ] && return
    for module in $(cat /etc/modules); do
      progress "Loading kernel module $module"
      insmod "$MODULE_DIR/$module.ko" || \
        progress "... Failed to load kernel module $module, skipping"
    done
  }

  load_splash() {
    local set_default_res=no
    local vres

    if [ ! "$SPLASH" = "no" ]; then
      progress "Loading bootsplash"

      # load uvesafb module if needed
      if [ -f "$MODULE_DIR/uvesafb.ko" -a ! -e /dev/fb0 ]; then
        progress "Loading kernel module uvesafb.ko"
        insmod "$MODULE_DIR/uvesafb.ko" && \
          set_default_res=yes || \
          progress "... Failed to load kernel module uvesafb, skipping"
      fi

      if [ -e /dev/fb0 ]; then
        # Set framebuffer to a custom resolution and/or fallback to default resolution (1024x768-32), if required.
        if [ ! "$SWITCH_FRAMEBUFFER" = "no" ]; then
          if [ "$SWITCH_FRAMEBUFFER" = "1080" ]; then
            SWITCH_FRAMEBUFFER="1920 1080 1920 1080 32"
          elif [ "$SWITCH_FRAMEBUFFER" = "720" ]; then
            SWITCH_FRAMEBUFFER="1280 720 1280 720 32"
          fi

          # Try setting a custom framebuffer resolution
          if [ ! "${SWITCH_FRAMEBUFFER:-yes}" = "yes" ]; then
            fbset -g $SWITCH_FRAMEBUFFER 2>/dev/null && set_default_res=no
          fi

          # Set a default resolution if required
          if [ "$set_default_res" = "yes" ]; then
            fbset -g 1024 768 1024 768 32
          fi
        fi

        # load splash
        if [ -f /splash/splash.conf ]; then
          . /splash/splash.conf
        fi

        # Select splash image based on current native resolution
        if [ -z "$SPLASHIMAGE" ]; then
          vres="$(fbset 2>/dev/null | awk '/geometry/ { print $3 }')"

          if [ -f /flash/oemsplash.png -o -f /flash/oemsplash-1080.png ]; then
            if [ -n "$vres" -a -f /flash/oemsplash-$vres.png ]; then
              SPLASHIMAGE="/flash/oemsplash-$vres.png"
            elif [ -f /flash/oemsplash-1080.png ]; then
              SPLASHIMAGE="/flash/oemsplash-1080.png"
            else
              SPLASHIMAGE="/flash/oemsplash.png"
            fi
          else
            if [ -n "$vres" -a -f /splash/splash-$vres.png ]; then
              SPLASHIMAGE="/splash/splash-$vres.png"
            else
              SPLASHIMAGE="/splash/splash-1080.png"
            fi
          fi
        fi

        if [ -n "$SPLASHIMAGE" -a -f "$SPLASHIMAGE" ]; then
          ply-image $SPLASHIMAGE > /dev/null 2>&1
        fi

        debug_msg "Framebuffer vertical res: $vres"
        debug_msg "Framebuffer splash image: $SPLASHIMAGE"
      fi
    fi
  }

  do_reboot() {
    echo "System reboots now..."

    # syncing filesystem
    sync

    # unmount filesystems
    if /bin/busybox mountpoint -q /flash ; then
      /bin/busybox umount /flash
    fi

    if /bin/busybox mountpoint -q /storage ; then
      /bin/busybox umount /storage
    fi

    usleep 2000000
    /bin/busybox reboot
  }

  force_fsck() {
    echo "Filesystem corruption has been detected!"
    echo "To prevent an automatic repair attempt continuing,"
    echo "press any key or power off your system within the next 120 seconds"
    echo ""
    read -t120 -n1
    # The exit status is 0 if input is available
    # The exit status is greater than 128 if the timeout is exceeded
    if [ "$?" -ne "0" -o "$?" -gt "128" ] ; then
      echo "Repairing filesystem..."
      echo ""
      /sbin/fsck -T -M -y $RUN_FSCK_DISKS
      FSCK_RET="$?"
      if [ "$(( $FSCK_RET & 8 ))" = 8 ] ; then
        # fubar
        echo "Forced fsck failed. Your system is broken beyond repair"
        echo "Please re-install @DISTRONAME@"
        echo ""
        echo "Press enter to shutdown now"
        echo ""
        read fubar
        poweroff
      fi
      do_reboot
    else
      echo "Shutting down..."
      sleep 5
      sync
      poweroff
    fi
  }

  check_disks() {
    if [ "$RUN_FSCK" = "yes" -a -n "$RUN_FSCK_DISKS" ]; then
      progress "Checking disk(s): $RUN_FSCK_DISKS"
      /sbin/fsck -T -M -p -a $RUN_FSCK_DISKS > /dev/null 2>&1
      FSCK_RET="$?"

      # FSCK_RET is the bit-wise OR of the exit codes for each filesystem that is checked.
      if [ "$(( $FSCK_RET & 4 ))" = 4 ] ; then
        # errors left
        force_fsck
      elif [ "$(( $FSCK_RET & 2 ))" = 2 ] ; then
        # reboot needed
        echo "Filesystem repaired, reboot needed..."
        do_reboot
      elif [ "$(( $FSCK_RET & 1 ))" = 1 ] ; then
        # filesystem errors corrected
        progress "Filesystem errors corrected , continuing..."
      elif [ "$(( $FSCK_RET & 0 ))" = 0 ] ; then
        # no errors found
        progress "No filesystem errors found, continuing..."
      fi
    fi
  }

  wakeonlan() {
    if [ "$STORAGE_NETBOOT" = "yes" ]; then
      wol_ip=${disk%:*}
      wol_ip=${wol_ip#*=}
    elif [ "$FLASH_NETBOOT" = "yes" ]; then
      wol_ip=${boot%:*}
      wol_ip=${wol_ip#*=}
    else
      return 0
    fi

    if [ -n "$wol_ip" -a -n "$wol_mac" -a -n "$wol_wait" ]; then
      progress "Sending Magic Packet (WOL) if needed"

      if ! ping -q -c 2 "$wol_ip" &>/dev/null; then
        ether-wake "$wol_mac"
        StartProgress countdown "WOL magic packet sent to $wol_ip, waiting $wol_wait seconds... " $wol_wait "done"
      fi
    fi
  }

  mount_flash() {
    progress "Mounting flash"

    wakeonlan

    mount_part "$boot" "/flash" "ro,noatime"
  }

  mount_storage() {
    progress "Mounting storage"

    if [ "$LIVE" = "yes" ]; then
      # mount tmpfs and exit early. disk=xx is not allowed in live mode
      mount -t tmpfs none /storage
      return
    fi

    wakeonlan

    if [ -n "$disk" ]; then
      if [ -n "$OVERLAY" ]; then
        OVERLAY_DIR=`cat /sys/class/net/eth0/address | tr -d :`

        mount_part "$disk" "/storage" "rw,noatime"
        mkdir -p /storage/$OVERLAY_DIR
        umount /storage

        # split $disk into $target,$options so we can append $OVERLAY_DIR
        options="${disk#*,}"
        target="${disk%%,*}"
        if [ "$options" = "$disk" ]; then
          disk="$target/$OVERLAY_DIR"
        else
          disk="$target/$OVERLAY_DIR,$options"
        fi
      fi
      mount_part "$disk" "/storage" "rw,noatime"
    else
      # /storage should always be writable
      mount -t tmpfs none /storage
    fi
  }

  do_cleanup() {
    StartProgress spinner "Cleaning up... "

    if [ -d $UPDATE_ROOT/.tmp/mnt ]; then
      if mountpoint -q $UPDATE_ROOT/.tmp/mnt ; then
        # busybox umount deletes loop device automatically
        umount $UPDATE_ROOT/.tmp/mnt
      fi

      [ -n $LOOP ] && losetup -d $LOOP &>/dev/null
    fi

    [ -f "$UPDATE_TAR" ] && rm -f "$UPDATE_TAR" &>/dev/null
    [ -f "$UPDATE_IMG_GZ" ] && rm -f "$UPDATE_IMG_GZ" &>/dev/null
    [ -f "$UPDATE_IMG" ] && rm -f "$UPDATE_IMG" &>/dev/null

    rm -rf $UPDATE_ROOT/.tmp &>/dev/null
    rm -rf $UPDATE_ROOT/[0-9a-zA-Z]* &>/dev/null

    sync

    StopProgress "done"
  }

  check_update() {
    progress "Checking for updates"
    UPDATE_TAR=`ls -1 "$UPDATE_DIR"/*.tar 2>/dev/null | head -n 1`
    UPDATE_IMG_GZ=`ls -1 "$UPDATE_DIR"/*.img.gz 2>/dev/null | head -n 1`
    UPDATE_IMG=`ls -1 "$UPDATE_DIR"/*.img 2>/dev/null | head -n 1`

    if ! [ -f "$UPDATE_DIR/$UPDATE_KERNEL" -a -f "$UPDATE_DIR/$UPDATE_SYSTEM" ] &&
       ! [ -f "$UPDATE_TAR" -o -f "$UPDATE_IMG_GZ" -o -f "$UPDATE_IMG" ]; then
      return 0
    fi

    if [ "$UPDATE_DISABLED" = "yes" ] ; then
      echo "Updating is not supported on netboot"
      do_cleanup
      StartProgress countdown "Normal startup in 10s... " 10 "NOW"
      return 0
    fi

    # remove temporary folder if exist from previous run
    rm -fr "$UPDATE_DIR/.tmp" &>/dev/null

    echo "UPGRADE IN PROGRESS"
    echo ""
    echo "Please do not reboot or turn off your @DISTRONAME@ device!"
    echo ""

    if [ -f "$UPDATE_TAR" ] ; then
      echo "Found new .tar archive"
      StartProgress spinner "Extracting contents of archive... "
        mkdir -p $UPDATE_DIR/.tmp &>/dev/null
        tar -xf "$UPDATE_TAR" -C $UPDATE_DIR/.tmp &>/dev/null
        mv $UPDATE_DIR/.tmp/*/target/* $UPDATE_DIR &>/dev/null
        sync
        StopProgress "done"
    elif [ -f "$UPDATE_IMG_GZ" -o -f "$UPDATE_IMG" ] ; then
      mkdir -p $UPDATE_DIR/.tmp/mnt &>/dev/null
      IMG_FILE="$UPDATE_DIR/.tmp/update.img"
      GZRESULT="0"

      if [ -f "$UPDATE_IMG_GZ" ]; then
        echo "Found new compressed image file"

        StartProgress spinner "Decompressing image file... "
          gunzip -d -c "$UPDATE_IMG_GZ" 1>$IMG_FILE 2>/tmp/gzresult.txt || GZRESULT="1"
          sync
          [ "${GZRESULT}" -eq "0" ] && StopProgress "OK" || StopProgress "FAILED"

        if [ "${GZRESULT}" -eq "1" ]; then
          echo "Failed to decompress image file!"
          echo "gunzip result: '$(cat /tmp/gzresult.txt)'"
          do_cleanup
          StartProgress countdown "Normal startup in 30s... " 30 "NOW"
          return 0
        fi
      else
        echo "Found new image file"
        mv "$UPDATE_IMG" $IMG_FILE
      fi

      LOOP=$(losetup -f)
      LOOP_NUM=$(echo $LOOP | sed 's|/dev/loop||')
      mknod $LOOP b 7 $LOOP_NUM &>/dev/null
      losetup $LOOP $IMG_FILE

      # check for MBR partititon
      OFFSET=$(fdisk -u -l $LOOP 2>/dev/null | awk '/^[ ]*Device/{part=1; next}; part{if ($2 == "*") {print $3} else {print $2} ; exit}')
      if [ -z "$OFFSET" ]; then
        # check for GPT partititon
        OFFSET=$(fdisk -u -l $LOOP 2>/dev/null | awk '/^Number/{part=1; next}; part{print $2; exit}')
        if [ -z "$OFFSET" ]; then
          echo "Could not find a valid system partition in image file!"
          do_cleanup
          StartProgress countdown "Normal startup in 10s... " 10 "NOW"
          return 0
        fi
      fi

      SECTOR_SIZE=$(cat /sys/devices/virtual/block/loop${LOOP_NUM}/queue/hw_sector_size)
      losetup -d $LOOP
      sync

      OFFSET=$(($OFFSET * $SECTOR_SIZE))

      # use losetup because busybox mount does not support the -o offset option
      echo "Mounting system partition..."
      losetup -o $OFFSET $LOOP $IMG_FILE
      mount -o ro,loop $LOOP $UPDATE_DIR/.tmp/mnt

      # don't make temporary files but instead copy
      # directly from mountpoint to /flash
      UPDATE_DIR=$UPDATE_ROOT/.tmp/mnt
      if [ ! -b $IMAGE_KERNEL -o -z "@KERNEL_NAME@" ]; then
        UPDATE_KERNEL=$(basename $IMAGE_KERNEL)
      else
        UPDATE_KERNEL="@KERNEL_NAME@"
      fi
    fi

    sync

    if [ ! -f "$UPDATE_DIR/$UPDATE_KERNEL" -o ! -f "$UPDATE_DIR/$UPDATE_SYSTEM" ] ; then
      echo "Missing ${UPDATE_KERNEL} or ${UPDATE_SYSTEM}!"
      do_cleanup
      StartProgress countdown "Normal startup in 10s... " 10 "NOW"
      return 0
    fi

    # check md5 sums if .nocheck doesn't exist
    if [ ! -f "$UPDATE_DIR/.nocheck" ]; then
      if [ -f "$UPDATE_DIR/${UPDATE_KERNEL}.md5" -a -f "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" ] ; then
        # *.md5 size-check
        if [ ! -s "$UPDATE_DIR/${UPDATE_KERNEL}.md5" -o ! -s "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" ] ; then
          echo "Zero-sized .md5 file!"
          MD5_FAILED="1"
        else
          sed "s#target/KERNEL#$UPDATE_DIR/$UPDATE_KERNEL#g" "$UPDATE_DIR/${UPDATE_KERNEL}.md5" >"$UPDATE_ROOT/${UPDATE_KERNEL}.check.md5"
          sed "s#target#$UPDATE_DIR#g" "$UPDATE_DIR/${UPDATE_SYSTEM}.md5" >"$UPDATE_ROOT/${UPDATE_SYSTEM}.check.md5"

          StartProgress spinner "Checking ${UPDATE_KERNEL}.md5... "
            if md5sum -sc "$UPDATE_ROOT/${UPDATE_KERNEL}.check.md5"; then
              StopProgress "OK"
            else
              StopProgress "FAILED"
              MD5_FAILED="1"
	    fi

          StartProgress spinner "Checking ${UPDATE_SYSTEM}.md5... "
            if md5sum -sc "$UPDATE_ROOT/${UPDATE_SYSTEM}.check.md5"; then
              StopProgress "OK"
            else
              StopProgress "FAILED"
              MD5_FAILED="1"
            fi
        fi
      else
        echo "Missing ${UPDATE_KERNEL}.md5 or ${UPDATE_SYSTEM}.md5!"
        MD5_FAILED="1"
      fi

      if [ "$MD5_FAILED" -eq "1" ]; then
        echo "md5 check failed!"
        do_cleanup
        StartProgress countdown "Normal startup in 30s... " 30 "NOW"
        return 0
      fi
    fi

    # get sizes
    FLASH_FREE=$(df /flash/ | awk '/[0-9]%/{print $4}')
    FLASH_FREE=$(( $FLASH_FREE * 1024 ))

    # Disregard kernel size if it's a a block device, which is the case on Amlogic/WeTek devices
    if [ ! -b $IMAGE_KERNEL ]; then
      OLD_KERNEL=$(stat -t "/flash/$IMAGE_KERNEL" | awk '{print $2}')
    else
      OLD_KERNEL="0"
    fi

    OLD_SYSTEM=$(stat -t "/flash/$IMAGE_SYSTEM" | awk '{print $2}')
    NEW_KERNEL=$(stat -t "$UPDATE_DIR/$UPDATE_KERNEL" | awk '{print $2}')
    NEW_SYSTEM=$(stat -t "$UPDATE_DIR/$UPDATE_SYSTEM" | awk '{print $2}')

    # old KERNEL+SYSTEM+free space - new KERNEL+SYSTEM must be higher then 5MB
    # at least 5MB free after update

    TMP_SIZE=$(($OLD_KERNEL+$OLD_SYSTEM+$FLASH_FREE-$NEW_KERNEL-$NEW_SYSTEM))
    FLASH_FREE_MIN=$(($FLASH_FREE_MIN*1024*1024))

    if [ $TMP_SIZE -ge $FLASH_FREE_MIN ]; then
      echo "Checking size: OK"
    else
      echo "Checking size: FAILED"
      echo ""
      echo "Your System (FAT) partition is too small for this update,"
      echo "and there is not enough space for the update to be installed!"
      echo ""
      echo "You must re-install your system using the disk image of a"
      echo "current release, or you must re-size your existing partitions"
      echo "so that the System (FAT) partition is at least 512MB in size."
      echo ""
      do_cleanup
      StartProgress countdown "Normal startup in 60s... " 60 "NOW"
      return 0
    fi

    # all ok, update
    if [ -b $IMAGE_KERNEL ]; then
      update_partition "Kernel" "$UPDATE_KERNEL" "$IMAGE_KERNEL"
    else
      update_file "Kernel" "$UPDATE_KERNEL" "/flash/$IMAGE_KERNEL"
    fi
    update_file "System" "$UPDATE_SYSTEM" "/flash/$IMAGE_SYSTEM"
    update_bootloader
    do_cleanup
    do_reboot
  }

  prepare_sysroot() {
    progress "Preparing system"

    if [ "$SYSTEM_TORAM" = "no" -o "$INSTALLED_MEMORY" -lt "$SYSTEM_TORAM_LIMIT" ]; then
      mount_part "/flash/$IMAGE_SYSTEM" "/sysroot" "ro,loop"
    else
      cp /flash/$IMAGE_SYSTEM /dev/$IMAGE_SYSTEM
      mount_part "/dev/$IMAGE_SYSTEM" "/sysroot" "ro,loop"
    fi

    mount --move /flash /sysroot/flash
    mount --move /storage /sysroot/storage

    if [ ! -d "/sysroot/lib/modules/$(uname -r)/" -a -f "/sysroot/usr/lib/systemd/systemd" ]; then
      echo ""
      echo "NEVER TOUCH boot= in extlinux.conf / cmdline.txt!"
      echo "If you don't know what you are doing,"
      echo "your installation is now broken."
      echo ""
      StartProgress countdown "Normal startup in 60s... " 60 "NOW"
    fi

    [ -f "/sysroot/usr/lib/systemd/systemd" ] || error "final_check" "Could not find systemd!"
  }

  if [ "${boot%%=*}" = "FILE" ]; then
    error "check arguments" "boot argument can't be FILE type..."
  fi

  # main boot sequence
  for BOOT_STEP in \
      load_modules \
      check_disks \
      mount_flash \
      load_splash \
      mount_storage \
      check_update \
      prepare_sysroot; do
    $BOOT_STEP
    [ -n "$DEBUG" ] && break_after $BOOT_STEP
  done

  BOOT_STEP=final

  # log if booting from usb / removable storage
  STORAGE=$(cat /proc/mounts | grep " /sysroot/storage " | awk '{print $1}' | awk -F '/' '{print $3}')
  FLASH=$(cat /proc/mounts | grep " /sysroot/flash " | awk '{print $1}' | awk -F '/' '{print $3}')
  for i in $STORAGE $FLASH ; do
    if [ -n "$i" ] ; then
      removable="/sys/class/block/*/$i/../removable"
      if [ -e $removable ] ; then
        if [ "$(cat $removable 2>/dev/null)" = "1" ] ; then
          echo "### BIG FAT WARNING" > /dev/kmsg
          echo "### $i is removable. suspend/resume may not work" > /dev/kmsg
        fi
      fi
    fi
  done
  # move some special filesystems
  /bin/busybox mount --move /dev /sysroot/dev
  /bin/busybox mount --move /proc /sysroot/proc
  /bin/busybox mount --move /sys /sysroot/sys
  /bin/busybox rm -fr /tmp

  # tell OE settings addon to disable updates
  if [ "$UPDATE_DISABLED" = "yes" ] ; then
    echo "" > /sysroot/dev/.update_disabled
  fi
  # swap can not be used over nfs.(see scripts/mount-swap)
  if [ "$STORAGE_NETBOOT" = "yes" ] ; then
    echo "" > /sysroot/dev/.storage_netboot
  fi

  if [ -f /sysroot/storage/.please_resize_me ] ; then
    INIT_UNIT="--unit=fs-resize.target"
  fi

  BACKUP_FILE=`ls -1 /sysroot/storage/.restore/??????????????.tar 2>/dev/null | head -n 1`
  if [ -f "$BACKUP_FILE" ] ; then
    INIT_UNIT="--unit=backup-restore.target"
  fi

  if [ -f /sysroot/storage/.cache/reset_oe -o -f /sysroot/storage/.cache/reset_xbmc ] ; then
    INIT_UNIT="--unit=factory-reset.target"
  fi

  # switch to new sysroot and start real init
  exec /bin/busybox switch_root /sysroot /usr/lib/systemd/systemd $INIT_ARGS $INIT_UNIT

  error "switch_root" "Error in initramfs. Could not switch to new root"
