#!/bin/bash

function usage {
  echo "$(basename "$0") [OPTIONS]"
  echo "  -h            shows usage"
  echo "  -g <version>  generate UKI image for specified kernel version"
  echo "  -a            generate UKI images for all available kernels"
}

function check_root {
  [ $EUID -eq 0 ] && return
  echo "dracut-initramfs requires root privileges to work" >&2
  exit 1
}

if [[ ${#} -eq 0 ]]; then
   usage
fi

ESP_PATH=$(bootctl --print-esp-path)
if [ -z "$ESP_PATH" ]; then
  exit 1
else

  if [ ! -d "${ESP_PATH}/EFI/Linux" ]; then
    if [ -d "${ESP_PATH}/EFI/linux" ]; then
      BOOT_PATH="${ESP_PATH}/EFI/linux"
    fi
  else
    BOOT_PATH="${ESP_PATH}/EFI/Linux"
  fi

  if [ -z "$BOOT_PATH" ]; then
    mkdir -p "${ESP_PATH}/EFI/Linux"
    BOOT_PATH="${ESP_PATH}/EFI/linux"
  fi

fi

declare -A kernels
update_all=0

while getopts ":hag:xyz" arg; do
  case ${arg} in
    g)
      found=0
      for line in $(pacman -Qql "$OPTARG"); do
        if [[ $line =~ ^/usr/lib/modules/([^/]+)/pkgbase$ ]]; then
          read -r kernel_name < "/${line}"
          kernels["${kernel_name}"]="${BASH_REMATCH[1]}"
          found=1
          break
        fi
      done
      if (( ! found )); then
        echo "Error occurred during '$OPTARG' package traversal" >&2
        exit 1
      fi
      ;;
    a)
      update_all=1
      ;;
    x)
      check_root
      # Trigger some IO on ESP path to be sure it's mounted by autofs if it's the case
      # Otherwise upgrading systemd may cause ESP partition not mounted at the time dracut attempt to write new image
      stat "$ESP_PATH" >/dev/null
      ;;
    y)
      check_root
      while read -r line; do
        if [[ "$line" == 'usr/lib/modules/'+([^/])'/pkgbase' ]]; then
          read -r kernel_name < "/${line}"
          path="$(grep -lE "^${kernel_name}\$" /usr/lib/modules/*/pkgbase)"
          kernel_version=$(basename "${path%/pkgbase}")

          kernel_image="$BOOT_PATH/vmlinuz-$kernel_name"
          initrd_image="$BOOT_PATH/initramfs-$kernel_name.img"
          initrd_image_fallback="$BOOT_PATH/initramfs-$kernel_name-fallback.img"
          initrd_image_fallback_terminal="$BOOT_PATH/initramfs-$kernel_name-fallback-terminal.img"

          declare -a images=()
          images+=("$kernel_image")
          images+=("$initrd_image")
          images+=("$initrd_image_fallback")
          images+=("$initrd_image_fallback_terminal")

          for image in "${images[@]}";
          do
            if [ -f "$image" ]; then
              echo "==> Removing $image..."
              rm -f "$image"
            fi
          done
        fi
      done
      exit 0
      ;;
    z)
      check_root
      while read -r line; do
        if [[ $line =~ ^usr/lib/modules/([^/]+)/pkgbase$ ]]; then
          read -r kernel_name < "/${line}"
          kernels["${kernel_name}"]="${BASH_REMATCH[1]}"
        else
          update_all=1
          break 
        fi
      done
      ;;
    h)
      usage
      ;;
    *)
      usage
      ;;
  esac
done

if (( update_all )); then
  for kernel_path in /usr/lib/modules/*; do
    [ -f "$kernel_path/pkgbase" ] || continue

    kernel_version=$(basename "$kernel_path")
    kernel_dir="/usr/lib/modules/$kernel_version"
    pkgbase="$kernel_dir/pkgbase"
    kernel_name=$(sed -e 's/^[[:space:]]//g' -e 's/[[:space:]]$//g' "$pkgbase")

    if [ -n "$kernel_name" ]; then
      kernels["$kernel_name"]="$kernel_version"
    fi
  done
fi

function gen_image() {
  check_root
  kernel_name="$1"
  kernel_version="$2"

  kernel_dir="/usr/lib/modules/$kernel_version"

  if [ -f "/etc/kernel/cmdline" ]; then
    cmdline=$(sed -e 's/^[[:space:]]//g' -e 's/[[:space:]]$//g' "/etc/kernel/cmdline" | tr '[:space:]' ' ' | tr -s ' ')

  elif [ -f "/boot/refind_linux.conf" ]; then
      cmdline=$(head -n 1 < "/boot/refind_linux.conf" | sed -e 's|"Boot with standard options"||g' | awk '{$1=$1;print}' | sed -e 's/"//g')

  elif [ -f "/efi/refind_linux.conf" ]; then
      cmdline=$(head -n 1 < "/efi/refind_linux.conf" | sed -e 's|"Boot with standard options"||g' | awk '{$1=$1;print}' | sed -e 's/"//g')

  elif [ -f "/boot/efi/refind_linux.conf" ]; then
      cmdline=$(head -n 1 < "/boot/efi/refind_linux.conf" | sed -e 's|"Boot with standard options"||g' | awk '{$1=$1;print}' | sed -e 's/"//g')

  else
    cmdline=$(sed -e 's/^[[:space:]]//g' -e 's/[[:space:]]$//g' -e 's/initrd.*$//g' "/proc/cmdline")
  fi

  cmdline_terminal="$cmdline systemd.unit=multi-user.target"

  vmlinuz="$kernel_dir/vmlinuz"
  kernel_image="$BOOT_PATH/vmlinuz-$kernel_name"

  initrd_image="$BOOT_PATH/initramfs-$kernel_name.img"
  initrd_image_fallback="$BOOT_PATH/initramfs-$kernel_name-fallback.img"
  initrd_image_fallback_terminal="$BOOT_PATH/initramfs-$kernel_name-fallback-terminal.img"

  echo "==> Building initrd image $kernel_name $initrd_image"
  #dracut --force --hostonly --kver "$kernel_version" --kernel-cmdline="$cmdline" "$BOOT_PATH/initramfs-$kernel_name.img"
  #dracut --force --hostonly --no-hostonly-cmdline --kver "$kernel_version" "$BOOT_PATH/initramfs-$kernel_name.img"
  dracut --force --hostonly --no-hostonly-i18n --early-microcode --kernel-cmdline="$cmdline" --kver "$kernel_version" "$initrd_image"

  echo "==> Building initrd image $kernel_name $initrd_image_fallback"
  #dracut --force --no-hostonly --kver "$kernel_version" --kernel-cmdline="$cmdline" "$BOOT_PATH/initramfs-$kernel_name-fallback.img"
  #dracut --force --kver "$kernel_version" "$BOOT_PATH/initramfs-$kernel_name-fallback.img"
  dracut --force --no-hostonly --no-hostonly-i18n --early-microcode --kernel-cmdline="$cmdline" --kver "$kernel_version" "$initrd_image_fallback"

  echo "==> Building initrd image $kernel_name $initrd_image_fallback_terminal TERMINAL"
  #dracut --force --no-hostonly --kver "$kernel_version" --kernel-cmdline="$cmdline" "$BOOT_PATH/initramfs-$kernel_name-fallback.img"
  #dracut --force --kver "$kernel_version" "$BOOT_PATH/initramfs-$kernel_name-fallback.img"
  dracut --force --no-hostonly --no-hostonly-i18n --early-microcode --kernel-cmdline="$cmdline_terminal" --kver "$kernel_version" "$initrd_image_fallback_terminal"

  cp -vrf "$vmlinuz" "$kernel_image"
}

for kernel_name in "${!kernels[@]}"; do
  kernel_version="${kernels[$kernel_name]}"
  gen_image "$kernel_name" "$kernel_version"
done