#!/bin/bash
####
# This script load zfs kernel module for any archiso
####
# @since: 2020-05-01
# @author:
#  eoli3n <https://eoli3n.eu.org/about/>
#  stevleibelt <artodeto@bazzline.net>
#
# Thanks to CalimeroTeknik on #archlinux-fr, FFY00 on #archlinux-projects, JohnDoe2 on #regex
####

exec &> >(tee "debug.log")

### Vars

verbose=0

### Functions

ask () {
    read -p "> $1 " -r
    echo
}

usage () {
    cat << EOF
Usage: ${0##*/} [-v]

    -v    increase verbosity
    -h    show this usage
EOF
}

print () {
    echo -e "\n\033[1m> $1\033[0m"
}

download_and_execute_arch-config () {
    ask "Do you want to download and execute arch-config?"
    if [[ $REPLY =~ ^[Yy]$ ]]
    then

        if [[ ! -f /usr/bin/git ]]
        then

            pacman -S git --noconfirm &>/dev/null

        fi

        git clone https://github.com/stevleibelt/arch-linux--configuration
        cd arch-linux-zfs-configuration/scripts/zfs/install
        ./01-configure.sh
        ./02-install.sh

    fi
}

get_running_kernel_version () {
# Returns running kernel version

    # Get running kernel version
    kernel_version=$(uname -r)
    
    print "Current kernel version is $kernel_version"
}

init_archzfs () {
    if pacman -Sl archzfs >&3; then
        print "archzfs repo was already added"
        return 0
    fi
    print "Add archzfs repo"
    
    # Disable Sig check
    pacman -Syy archlinux-keyring --noconfirm >&3 || return 1
    pacman-key --populate archlinux >&3 || return 1
    pacman-key --recv-keys F75D9D76 >&3 || return 1
    pacman-key --lsign-key F75D9D76 >&3 || return 1
    cat >> /etc/pacman.conf <<"EOF"
[archzfs]
Server = http://archzfs.com/archzfs/x86_64
Server = http://mirror.sum7.eu/archlinux/archzfs/archzfs/x86_64
Server = https://mirror.biocrafting.net/archlinux/archzfs/archzfs/x86_64
EOF
    pacman -Sy >&3 || return 1
    return 0
}

init_archlinux_archive () {
# $1 is date formated as 'YYYY/MM/DD'
# Returns 1 if repo does not exists

    # Archlinux Archive workaround for 2022/02/01
    if [[ "$1" == "2022/02/01" ]]
    then
        version="2022/02/02"
    else
        version="$1"
    fi

    # Set repo
    repo="https://archive.archlinux.org/repos/$version/"

    # If repo exists, set it
    if curl -s "$repo" >&3
    then
        echo "Server=$repo\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
    else
        print "Repository $repo is not reachable or doesn't exist."
        return 1
    fi

    return 0
}

search_package () {
# $1 is package name to search
# $2 is version to match

    # Set regex to match package
    local regex='href="\K(?![^"]*\.sig)'"$1"'-(?=\d)[^"]*'"$2"'[^"]*x86_64[^"]*'
    # href="               # match href="
    # \K                   # don't return anything matched prior to this point
    # (?![^"]*\.sig)       # remove .sig matches
    # '"$1"'-(?=\d)        # find me '$package-' escaped by shell and ensure that after "-" is a digit
    # [^"]*                # match anything between '"'
    # '"$2"'               # match version escaped by shell
    # [^"]*                # match anything between '"'
    # x86_64               # now match architecture
    # [^"]*                # match anything between '"'
    
    # Set archzfs URLs list
    local urls="http://archzfs.com/archzfs/x86_64/ http://archzfs.com/archive_archzfs/"
    
    # Loop search
    for url in $urls
    do
    
        print "Searching $1 on $url..."
    
        # Query url and try to match package
        local package=$(curl -s "$url" | grep -Po "$regex" | tail -n 1)
    
        # If a package is found
        if [[ -n $package ]]
        then
    
            print "Package \"$package\" found"
    
            # Build package url
            package_url="$url$package"
            return 0
        fi
    done

    # If no package found
    return 1
}

download_package () {
# $1 is package url to download in tmp

    print "Download to $package_file ..."

    local filename="${1##*/}"

    # Download package in tmp
    cd /tmp
    curl -sO "$1" || return 1
    cd -

    # Set out file
    package_file="/tmp/$filename"

    return 0
}

dkms_init () {
# Init everything to be able to install zfs-dkms

    print "Init Archlinux Archive repository"
    archiso_version=$(sed 's-\.-/-g' /version)
    init_archlinux_archive "$archiso_version" || return 1

    print "Download Archlinux Archives package lists and upgrade"
    pacman -Syyuu --noconfirm >&3 || return 1

    print "Install base-devel"
    pacman -S --noconfirm base-devel linux-headers git >&3 || return 1

    return 0
}

### Getopts

while getopts "vh" option; do
    case "${option}" in
        v)
            verbose=$((verbose + 1))
            ;;
        h)
            usage
            exit 0
            ;;
        *)
            usage
            exit 0
            ;;
    esac
done
shift $((OPTIND-1))

### Verbose mode

if [[ "$verbose" -gt 0 ]]
then
    exec 3>&1
else
    exec 3>/dev/null
fi

### Main

# Test if archiso is running

if ! grep 'arch.*iso' /proc/cmdline >&3
then
    print "You are not running archiso, exiting."
    exit 1
fi

print "Increase cowspace to half of RAM"

mount -o remount,size=50% /run/archiso/cowspace >&3

# Init archzfs repository
init_archzfs || exit 1

# Search kernel package
# https://github.com/archzfs/archzfs/issues/337#issuecomment-624312576
get_running_kernel_version
kernel_version_fixed="${kernel_version//-/\.}"

# Search zfs-linux package matching running kernel version
if search_package "zfs-linux" "$kernel_version_fixed"
then

    zfs_linux_url="$package_url"

    # Download package
    download_package "$zfs_linux_url" || exit 1
    zfs_linux_package="$package_file"

    print "Extracting zfs-utils version from zfs-linux PKGINFO"

    # Extract zfs-utils version from zfs-linux PKGINFO
    zfs_utils_version=$(bsdtar -qxO -f "$zfs_linux_package" .PKGINFO | grep -Po 'depend = zfs-utils=\K.*')

    # Search zfs-utils package matching zfs-linux package dependency
    if search_package "zfs-utils" "$zfs_utils_version"
    then
        zfs_utils_url="$package_url"

        print "Installing zfs-utils and zfs-linux"

        # Install packages
        if pacman -U "$zfs_utils_url" --noconfirm >&3 && pacman -U "$zfs_linux_package" --noconfirm >&3
        then
            zfs=1
        fi
    fi
else

    # DKMS fallback
    print "No zfs-linux package was found for current running kernel, fallback on DKMS method"
    dkms_init

    print "Install zfs-dkms"
    
    # Install package
    if pacman -S zfs-dkms --noconfirm >&3
    then
        zfs=1
    fi
fi

# Load kernel module
if [[ "$zfs" == "1" ]]
then

    modprobe zfs && echo -e "\n\e[32mZFS is ready\n"

    download_and_execute_arch-config

else
    print "No ZFS module found"
fi
