#!/bin/bash

DEBUG=0
ROOTDEV=''

function screen_init {
  # Clear screen
  clear

  # Output message file if it exists
  [[ -f /etc/msg ]] && cat /etc/msg
}


#
# Mounts the fakeroot from the kernel "root" param
#
function mount_fakeroot {
  local rootdev=${1}
  local fakeroot=${2}

  mount -o ro ${rootdev} ${fakeroot}
}


function log {
  local msg="${1}"
  echo -e "${msg}"
  if [[ ${DEBUG} -ne 0 ]]; then
    echo "Press enter to continue"
    read
  fi
}
 
 
#
# Resolves the path to the root device from the cmdline root= syntax.
# Currently handles UUID syntax and /dev/sdx syntax
#
function parse_cmdline {
  local cmdline=${1}

  dev=''
  for i in ${cmdline[@]}; do
    case "${i}" in
      root=*)
        if [[ ${i:5:4} == 'UUID' ]]; then
          #mount by uuid
          ROOTDEV="/dev/disk/by-uuid/$(echo ${i} | cut -d '=' -f 3)"
        else
          # mount by dev
          ROOTDEV="$(echo ${i} | cut -d '=' -f 2)"
        fi
      ;;
      initdebug)
        # Enable debug mode (this is gonna be slow
        DEBUG=1
      ;;
    esac
  done
}
 
 
#
# Returns type of partition. If part is LUKS encrypted, returns 'luks'. If
# anything else, returns 'fs'.
#
# Most installations of the mount command don't require specification of the
# filesystem, but won't automatically detect luks partitions and prompt for a
# password. This allows the script to be smarter about encrypted partitions.
#
function get_part_type {
  path=${1}
  typestr=$(blkid -s TYPE ${path})
  if [[ $(echo ${typestr} | grep 'crypto_LUKS') ]]; then
    echo 'luks'
  else
    echo 'fs'
  fi
}


#
# Takes an encrypted device path and executes cryptsetup luksOpen. Returns path
# to the new decrypted block device. This path takes the rough form of
# /dev/mapper/_dev_sdx
#
function setup_encrypted {
  path=${1}
  name=$(echo ${path} | tr / _)

  # Decrypted block dev path is /dev/mapper/${name}
  cryptsetup luksOpen ${path} ${name}

  # Notify user and wait for input on failure
  if [[ $? -gt 0 ]]; then
    echo "An error was detected mounting the encrypted root." >&2
    echo "Pausing. Press enter to continue." >&2
    read
  fi

  # Success. Return the path of the decrypted root device
  echo "/dev/mapper/${name}"
}


#
# Main function to keep the main operations code nicely separated from the
# rest.
#
function main {
  # display fanciful boot image
  screen_init

  # Mount the /proc and /sys filesystems.
  mount -t devtmpfs none /dev
  mount -t proc     none /proc
  mount -t tmpfs    none /run
  mount -t sysfs    none /sys

  local fakeroot='/mnt/root'

  if [[ ! -d ${fakeroot} ]]; then
    log "Fake root location ${fakeroot} does not exist. Creating."
    mkdir ${fakeroot}
  fi

  log "Parsing cmdline arguments"

  parse_cmdline "$(cat /proc/cmdline)"

  log "Root device: ${ROOTDEV}"

  if [[ $(get_part_type ${ROOTDEV}) == 'luks' ]]; then
    # Set new rootdev location (/dev/mapper/something). This will update it to
    # the decrypted block device path.
    log "Root device ${ROOTDEV} is encrypted."
    ROOTDEV=$(setup_encrypted ${ROOTDEV})
    log "New rootdev: ${ROOTDEV}"
  fi
  
  # Mount the fakeroot.
  log "Mounting fakeroot"
  mount_fakeroot ${ROOTDEV} ${fakeroot}

  # Clean up.
  # We actually don't do this because switch_root does this for us
  umount /proc
  umount /sys
  umount /run

  # Boot the real McCoy
  exec switch_root ${fakeroot} /sbin/init
  if [[ $? -gt 0 ]]; then
    log "There was an error performing switch_root to ${fakeroot}."
    log "Starting recovery shell."
    /bin/bash
  fi
}

main ${@}