#!/bin/bash

# Set color commands used with echo
# Refer to man console_codes for more info
NORMAL="\\033[0;39m"         # grey
SUCCESS="\\033[1;32m"        # green
WARNING="\\033[1;33m"        # yellow
FAILURE="\\033[1;31m"        # red
INFO="\\033[1;36m"           # light cyan

TTY_DEFAULT="/dev/console"
TTYS="/dev/console /dev/tty0 /dev/ttyS0 /dev/tty1 /dev/ttyS1 /dev/tty2 /dev/ttyS2"
TTY="${TTY_DEFAULT}"

discover_tty() {
    found=0
    for tty in ${TTYS}
    do
        echo -e "" >> "${tty}" 2>/dev/null
        if [ "$?" -eq 0 ] ; then
            TTY="${tty}"
            found=1
            break
        fi
    done

    if [ "${found}" -eq 1 ] ; then
        echo_tty_kmsg "${INFO}Using ${TTY} for output${NORMAL}"
    else
        TTY="${TTY_DEFAULT}"
        echo_tty_kmsg "${FAILURE}Defaulting to ${TTY} for output${NORMAL}"
    fi
}

# Prints msg to TTY and kmsg, stripping color codes for kmsg
# This echos twice - once for tty0 and once on kmsg for all others
# $1 - Message string to print
# Explicitly send boot messages to tty0, /dev/console is ttyS0
echo_tty_kmsg() {
    echo -e "$1" > "${TTY}"
    echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g' > /dev/kmsg
}

# This is the end point for fatal errors, repeats the fatal message every
# 30 seconds forever
# $1 - message to be printed out to user on failure
shell_trap() {
    local msg="$1"
    while true; do
        echo_tty_kmsg "Unable to boot Clear Linux*."
        echo_tty_kmsg "[${FAILURE} FAIL ${NORMAL}] FATAL: $msg"
        sleep 30
    done
}

# return details of the first CPU only
get_cpuinfo() {
    cat /proc/cpuinfo | awk 'BEGIN { RS = "" ; } { printf ("%s\n", $0); exit(0); }'
}

have_cpu_feature() {
    local feature="$1"
    get_cpuinfo | egrep -q "^flags.*\<$feature\>"
}

# Checks results, use only with missing CPU features
# $1 - Return code from previously run check
# $2 - Message to print to user - specific to CPU feature missing
check_result() {
    local ret="$1"
    local msg="$2"
    [ "$ret" -ne 0 ] && { echo_tty_kmsg "[${FAILURE} FAIL ${NORMAL}] $msg"; shell_trap "Detected Missing Required CPU Feature: $msg"; }
    echo_tty_kmsg "[${SUCCESS}  OK  ${NORMAL}] $msg"
}

have_ssse3_cpu_feature () {
    local feature="ssse3"
    local desc="Supplemental Streaming SIMD Extensions 3"
    local need="$desc ($feature)"
    have_cpu_feature "$feature"
    check_result "$?" "$need"
}

have_pclmul_cpu_feature () {
    local feature="pclmulqdq"
    local desc="Carry-less Multiplication extensions"
    local need="$desc ($feature)"
    have_cpu_feature "$feature"
    check_result "$?" "$need"
}

have_sse41_cpu_feature () {
    local feature="sse4_1"
    local desc="Streaming SIMD Extensions v4.1"
    local need="$desc ($feature)"
    have_cpu_feature "$feature"
    check_result "$?" "$need"
}

have_sse42_cpu_feature () {
    local feature="sse4_2"
    local desc="Streaming SIMD Extensions v4.2"
    local need="$desc ($feature)"
    have_cpu_feature "$feature"
    check_result "$?" "$need"
}

have_64bit_cpu() {
    local feature="lm" # "Long mode"
    local desc="64-bit CPU"
    local need="$desc ($feature)"
    have_cpu_feature "$feature"
    check_result "$?" "$need"
}

# Mounts the rootfs on a loopback device
# $1 - path to the media device the rootfs is stored on
mount_root() {
    local installer=${1}
    mkdir /mnt/media
    mount --read-only $installer /mnt/media
    local rootfsloop=$(losetup -fP --show /mnt/media/images/rootfs.img)
    if [ -n "${rootfsloop}" ]; then
        mkdir /mnt/rootfs
        mount --read-only ${rootfsloop} /mnt/rootfs
    else
        echo_tty_kmsg "[${FAILURE} FAIL ${NORMAL}] Failed to initialize loopback device for rootfs.img."
    fi
}

wait_for_timeout() {
    local n="$1"
    local space=" "
    echo_tty_kmsg
    echo_tty_kmsg
    while [ "$n" -ge 0 ] ; do
        printf "Continue booting in [%2d] sec(s)...\r" "$n" > "${TTY}"
        sleep 1
        n=`expr "$n" - 1`
    done
    printf "%50c\r" "$space" > "${TTY}"
}

check_iso_verify() {
    local ret="$1"
    local msg="$2"
    [ "$ret" -ne 0 ] && { echo_tty_kmsg "[${FAILURE} FAIL ${NORMAL}]"; shell_trap "$msg"; }
    [ "$ret" -eq 0 ] && { echo_tty_kmsg "[${SUCCESS}  OK  ${NORMAL}] $msg"; wait_for_timeout 10; }
}

# Check ISO media integrity
verify_media() {
    local installer="$1"
    local need="ISO integrity check"

    if [ -n "${installer}" ]; then
        checkisomd5 --verbose ${installer} > "${TTY}"
        check_iso_verify "${PIPESTATUS[0]}" "$need"
    else
        shell_trap "[${FAILURE} FAIL ${NORMAL}] Failed to verify installer media, failed to boot Clear Linux*."
    fi
}

# Finds the installer media
find_installer() {
    local retries=0
    local max_retries=10

    while [ $retries -le $max_retries ]; do
        installer=$(blkid -L CLR_ISO)
        if [ -n "${installer}" ]; then
            echo_tty_kmsg "[${SUCCESS}  OK  ${NORMAL}] Found installer media, continuing boot..."
            echo "$installer"
            break
        else
            echo_tty_kmsg "Searching for installer media, retrying..."
            sleep 1
            (( retries++ ))
        fi
        blkid | while read partition; do
            echo_tty_kmsg "${partition}"
        done
    done

    if [ $retries -ge $max_retries ]; then
        shell_trap "[${FAILURE} FAIL ${NORMAL}] Failed to find installer media, retries exhausted, failed to boot Clear Linux*."
    fi
}

# Mounts the installer media found previously
mount_installer() {
    local installer="$1"

    if [ -n "${installer}" ]; then
        mount_root "${installer}"
    else
        shell_trap "[${FAILURE} FAIL ${NORMAL}] Failed to mount installer media, failed to boot Clear Linux*."
    fi
}

overlay_and_switch() {
    mkdir /mnt/ramfs
    mount -t tmpfs -o size=512M none /mnt/ramfs
    mkdir -p /mnt/ramfs/w_root /mnt/ramfs/workdir /mnt/ramfs/rw_root
    mount -t overlay -o lowerdir=/mnt/rootfs,upperdir=/mnt/ramfs/w_root,workdir=/mnt/ramfs/workdir none /mnt/ramfs/rw_root

    # Switch root
    exec switch_root /mnt/ramfs/rw_root /sbin/init 1>/dev/kmsg 2>/dev/kmsg
}

check_kernel_cmdline_contains() {
    local optioncheck=${1}
    local kernelcmdline=$(cat /proc/cmdline)
    for option in $kernelcmdline; do
        if [ "$option" == "$optioncheck" ]; then
          return 0
        fi
    done
    return -1
}

main() {
    # Mount temp filesystem
    mount -t proc none /proc
    mount -t sysfs none /sys
    mount -t devtmpfs none /dev
    mount -t tmpfs none /run

    # insmod required modules
    {{range .Modules}}
    insmod {{.}}
    {{end}}

    discover_tty

    # Verify CPU features needed to run Clear exist
    echo_tty_kmsg "Checking if system is capable of running Clear Linux*..."
    have_64bit_cpu
    have_ssse3_cpu_feature
    have_sse41_cpu_feature
    have_sse42_cpu_feature
    have_pclmul_cpu_feature

    echo_tty_kmsg "[${SUCCESS}  OK  ${NORMAL}] All checks passed."

    # Find the installer
    installer=$(find_installer)

    # ISO media integrity check on installer media
    if check_kernel_cmdline_contains {{.IsoMediaBootOption}}; then
        echo_tty_kmsg "Starting ISO integrity check..."
        verify_media "$installer"
    fi

    # Mount it
    mount_installer "$installer"
    overlay_and_switch
}

main
