vbti_analysis/setup.sh
#!/usr/bin/bash

# Setup script for the PoC of VM Scape

set -u
set -e
set -E
set -o pipefail

trap 'log_err "Command failed at line $LINENO: $BASH_COMMAND"; log_err "Setup incomplete"' ERR

exec 3>&1

SCRIPT_DIR="$(realpath "$(dirname "$0")")"
cd "$SCRIPT_DIR"

KERNEL_PATH="${KERNEL_PATH:-$SCRIPT_DIR/LinuxKernel}"
KERNEL_REMOTE="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git"
# Commit that merges the VMScape patches
VMSCAPE_PATCH_REF="223ba8ee0a3986718c874b66ed24e7f87f6b8124"

# Path to patches for Selftest infrastructure
declare -a SELFTEST_PATCHES=( \
        "$SCRIPT_DIR/selftest_patches/0001-Integrate-uARF-into-Build-System.patch" \
        "$SCRIPT_DIR/selftest_patches/0002-Add-support-for-guest-user.patch" \
        "$SCRIPT_DIR/selftest_patches/0003-Add-uARF-selftests.patch" \
    )

KERNEL_SELFTEST_BR="vmscape"
KERNEL_BUILD_NAME="vmscape"
new_kernel_build_name="$KERNEL_BUILD_NAME-$(date +%y%m%d%H%M%S)"

UARF_PATH="$SCRIPT_DIR/../uARF"

source "$SCRIPT_DIR/util.sh"
source "$SCRIPT_DIR/kernel.sh"


function usage() {
    echo "Usage: $0 [OPTIONS]"
    echo ""
    echo "Options"
    echo "  -v, --verbose      Show verbose output."
    echo "  -h, --help         Show this help menu."
}

verbose=false

while [ "$#" -gt 0 ]; do
    case "$1" in
        -h|--help)
            usage
            exit
            ;;
        -v|--verbose)
            verbose=true
            shift
            ;;
        *)
            log_err "Invalid option \"$1\". Exit"
            usage
            exit 1
            ;;
    esac
done

# Install all packages that are not already installed
# $1 is a white-space separated list of packages
function install_deps() {
    local all_pkgs=$1

    local missing_pkgs=()
    for pkg in $all_pkgs; do
        if ! dpkg -s "$pkg" >/dev/null 2>&1; then
            missing_pkgs+=("$pkg")
        fi
    done

    if [ ${#missing_pkgs[@]} -eq 0 ]; then
        log_debug "All required packages are already installed."
        return 0
    fi

    log_info "Install missing packages: ${missing_pkgs[*]}"
    user_confirms || { log_info "Best of luck..."; return 0; }
    sudo apt-get install -y "${missing_pkgs[@]}"
}

# Patch the selftest infrastructure of kernel at $1 using patches $2
function patch_selftest() {
    local kernel_dir=$1
    local -n patches=$2
    log_info "Patching selftest infrastructure"

    cd "$kernel_dir"

    # Check if patches have already been applied (i.e. KERNEL_SELFTEST_BR exists)
    # Ask if they should be re-applied
    if git show-ref --quiet refs/heads/$KERNEL_SELFTEST_BR; then
        git switch "$KERNEL_SELFTEST_BR" --quiet
        log_info "It seems like patches have already been applied. Re-apply them?"
        if user_confirms; then
            git reset --hard "$VMSCAPE_PATCH_REF"
        else
            log_debug "Return without re-applying"
            return 0
        fi
    else
        git switch -c "$KERNEL_SELFTEST_BR" --quiet
    fi

    for e in "${patches[@]}"; do
        log_debug "Applying patches: $e"
        [ -f $e ] || { log_err "Patch $e does not exist"; exit 1; }
        git apply --check $e || { log_err "Patch $e does not apply"; exit 1; }
        git am --quiet < $e
    done
}

# Build and install uARF
function setup_uarf() {
    local uarf_dir=$1
    log_info "Setting up uARF"

    cd "$uarf_dir"
    make clean
    make -j "$(nproc)"
    log_info "Installing uARF system-wide. Requiring sudo"
    sudo make install > /dev/null
}

# Install all kernel deb files generated by make deb-pkg that contain the LOCALVERSION $1
function install_kernel() {
    local kernel_path=$1
    local build_name=$2

    deb_files=$(find "$kernel_path/.." -maxdepth 1 -name "linux*${build_name}*.deb" -print | tr '\n' ' ')
    log_info "Install kernel files: \n'$(echo $deb_files | tr ' ' '\n')'"
    user_confirms || { echo "Exit."; exit 1; }
    sudo dpkg -i $deb_files
}

# Check if a kernel with identifier $1 is installed
function is_kernel_installed() {
    local build_name=$1
    kernels=$(dpkg --list | grep "$build_name")
    echo "$kernels" | grep -q "linux-image" && echo "$kernels" | grep -q "linux-headers"
}

if ! verify_system; then
    log_err "Your system does not confirm with our verified setup. It could still work, but you would be on your own..."
    user_confirms || { echo "Exit."; exit 5; }
    log_info "Best of luck..."
fi

if is_kernel_installed "$KERNEL_BUILD_NAME"; then
    log_info "It seems like the setup has already be done on this host."
    user_confirms || { echo "Exit."; exit 5; }
fi

install_deps $KERNEL_DEP_PKGS

if ! kernel_prepare_src "$KERNEL_PATH" "$VMSCAPE_PATCH_REF"; then
    log_err "Kernel setup failed. Exit."
    exit 1
fi

patch_selftest $KERNEL_PATH SELFTEST_PATCHES

setup_uarf "$UARF_PATH"

#log_info "Building kernel for host"
#kernel_prepare_src "$KERNEL_PATH" "$KERNEL_SELFTEST_BR"
#kernel_config_host "$KERNEL_PATH"
#kernel_build "$KERNEL_PATH" "$new_kernel_build_name"
#install_kernel "$KERNEL_PATH" "$new_kernel_build_name"
#if ! is_kernel_installed "$new_kernel_build_name"; then
#    log_err "Failed to install kernel"
#    exit 1
#fi

log_info "Setup complete."
