#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

set -x
set -o errexit
set -o nounset
set -o pipefail
set -o errtrace

true "INFO: Currently running script: ${BASH_SOURCE[0]} $*"

MYDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

cd "$MYDIR"
cd ..
cd help-steps

source pre
source variables

## Debugging function.
apt_get_parse_unmet_dependency() {
   local pkg_unmet
   pkg_unmet="$1"
   true "INFO: Running \"dpkg -l | grep -- $pkg_unmet\"..."
   chroot_run dpkg -l | grep -- "$pkg_unmet" || true
   chroot_run apt-cache policy -- "$pkg_unmet" || true

   local line

   ## Thanks to:
   ## http://blog.edwards-research.com/2010/01/quick-bash-trick-looping-through-output-lines/

   set +x

   declare -A -g remember_pkg

   while read -r -d $'\n' line; do
      local unmet_dependency=""
      unmet_dependency="$(printf "%s\n" "$line" | grep -o "Depends:.*" | awk '{print $2}')" || true
      if [ "${unmet_dependency:-}" = "" ]; then
         ## no match
         continue
      else
         ## match
         if [ "${remember_pkg[$unmet_dependency]}" = "true" ]; then
            continue
         fi
         printf "%s\n" "${bold}${cyan}INFO: Found unmet dependency: $unmet_dependency. \
Will try to manually install it for debugging...${reset}"
         remember_pkg[$unmet_dependency]="true"
         set -x
         true "INFO: Running \"dpkg -l | grep $unmet_dependency\"..."
         chroot_run dpkg -l | grep -- "$unmet_dependency" || true
         chroot_run apt-cache policy -- "$unmet_dependency" || true
         pkg-install "$unmet_dependency" || true
         set +x
         printf "%s\n" "${bold}${cyan}INFO: Attempt to install unmet_dependency: $unmet_dependency done.${reset}"
         continue
      fi
   done < <( printf "%s\n" "$apt_get_output" )

   set -x
}

pkg-install() {
   exception_handler_setup "exception_handler_unchroot_unmount" ERR INT TERM

   local pkg_install_item
   pkg_install_item="$1"
   local skip_package
   for skip_package in $dist_build_script_skip_package_install; do
      if [ "${skip_package:-}" = "$pkg_install_item" ]; then
         unset skip_package
         true "${bold}${cyan}INFO: Skipping installation of '$pkg_install_item', because variable dist_build_script_skip_package_install includes it.${reset}"
         return 0
      fi
   done
   unset skip_package

   true "${cyan}INFO: Installing of '$*', because variable dist_build_script_skip_package_install does not include it... \
This may take a while...${reset}"

   ## apt: no way to view dpkg commandline and still run dpkg
   ## https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=52670
   local apt_get_exit_code="0"
   chroot_run \
         apt-get-noninteractive \
            "${DIST_APTGETOPT[@]}" \
            $apt_sourcelist_empty \
            $apt_sourceparts \
            $apt_unattended_opts \
            --yes \
            --no-install-recommends \
            install \
            "$@" \
            || { apt_get_exit_code="$?" ; true; };

   chroot_run sync
   $SUDO_TO_ROOT sync

   if [ "${apt_get_exit_code:-}" = "0" ]; then
      true "${cyan}INFO: Installed '$*', no error detected.${reset}"
      return 0
   fi

   true "${bold}${red}ERROR: Failed to install '$*'. (apt_get_exit_code: $apt_get_exit_code) \
Attempting to gather debug output to diagnose the problem...${reset}"

   true "${bold}${cyan}INFO: Read output of apt-get-noninteractive trying to install '$*' into a \
variable for debugging. This may take a while...${reset}"

   local apt_get_exit_code="0"
   apt_get_output=" \
         $( \
            chroot_run \
                  apt-get-noninteractive \
                     "${DIST_APTGETOPT[@]}" \
                     $apt_sourcelist_empty \
                     $apt_sourceparts \
                     $apt_unattended_opts \
                     --yes \
                     --no-install-recommends \
                     install \
                     "$@" \
                     2>&1 \
         ) \
      " \
      || { apt_get_exit_code="$?" ; true; };

   chroot_run sync
   sync

   if [ "${apt_get_exit_code:-}" = "0" ]; then
      true "${bold}${red}ERROR: Second attempt installing '$*' did not fail?!?${reset}"
      error "See above!"
      return 0
   fi

   true "${bold}${red}ERROR: As expected, failed again to install '$*'. (apt_get_exit_code: $apt_get_exit_code) \
Trying to diagnose the problem using function apt_get_parse_unmet_dependency...${reset}"

   apt_get_parse_unmet_dependency "$@"

   true "INFO: Tried to diagnose the problem using function apt_get_parse_unmet_dependency."

   error "See above!"
   return 0
}

pkg-add-to-install-list() {
   exception_handler_setup "exception_handler_unchroot_unmount" ERR INT TERM

   local pkg_install_item
   pkg_install_item="$1"
   local skip_package
   for skip_package in $dist_build_script_skip_package_install; do
      if [ "${skip_package:-}" = "$pkg_install_item" ]; then
         unset skip_package
         true "${bold}${cyan}INFO: Skipping package $pkg_install_item, because dist_build_script_skip_package_install includes it.${reset}"
         return 0
      fi
   done
   unset skip_package

   if [ "${pkg_install_item:-}" = "none" ]; then
      true "${bold}${cyan}INFO: Skipping package $pkg_install_item (none).${reset}"
      return 0
   fi

   if [ "${pkg_install_debug:-}" = "true" ]; then
      pkg-install "$pkg_install_item"
   else
      pkg_install_list+=" $pkg_install_item "
   fi
}

pkg-list-install() {
   exception_handler_setup "exception_handler_unchroot_unmount" ERR INT TERM

   if [ "${pkg_install_list:-}" = "" ]; then
      true "INFO: pkg_install_list still empty, ok."
      return 0
   else
      pkg-install $pkg_install_list
   fi
}

install-packages() {
   exception_handler_setup "exception_handler_unchroot_unmount" ERR INT TERM

   sync

   "$dist_source_help_steps_folder/mount-raw" "$@"

   ## Sanity test.
   test -d "$CHROOT_FOLDER/etc/default/grub.d"
   ## /etc/default/grub.d/20_dist-base-files.cfg
   ## https://www.kicksecure.com/wiki/Dev/boot#/etc/default/grub.d/20_dist-base-files.cfg
   ## https://www.kicksecure.com/wiki/Grub#/etc/default/grub.d/20_dist-base-files.cfg
   $SUDO_TO_ROOT cp -- "$source_code_folder_dist/packages/kicksecure/dist-base-files/usr/share/derivative-base-files/20_dist-base-files.cfg" "$CHROOT_FOLDER/etc/default/grub.d/20_dist-base-files.cfg"

   $SUDO_TO_ROOT cp -- "$source_code_folder_dist/packages/kicksecure/helper-scripts/usr/bin/apt-get-noninteractive" "$CHROOT_FOLDER/usr/bin/apt-get-noninteractive"

   "$dist_source_help_steps_folder/prevent-daemons-from-starting" "$@"

   sync

   ## Sanity tests.
   chroot_run sync
   chroot_run ls -la -- /

   sync

   "$dist_source_help_steps_folder/chroot-raw" "$@"
   "$dist_source_help_steps_folder/create-local-temp-apt-repo" "$@"

   ## Debugging.
   ## XXX: hardcoded
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/" || true
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/InRelease" || true
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/main/" || true
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/non-free/" || true
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/non-free/source" || true
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/non-free/binary-$dist_build_target_arch/" || true
   chroot_run ls -la -- "/$DEB_INSTALL_FOLDER/kicksecure/dists/local/non-free/binary-$dist_build_target_arch/Packages" || true

   "$dist_source_help_steps_folder/unchroot-raw" "$@"

   ## {{ controversy of: /etc/resolv.conf /etc/hosts /etc/hostname,
   ##    see help-steps/chroot-raw for more information.

   dist_chroot_mount_resolv_conf="0" "$dist_source_help_steps_folder/chroot-raw" "$@"
   "$dist_source_help_steps_folder/create-local-temp-apt-repo" "$@"

   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts update

#    if [ "${dist_build_target_arch:-}" = "arm64" ]; then
#       ## Install package helper-scripts first so pre.bsh and initramfs-debug-enable is available.
#       pkg-install helper-scripts
#
#       ## initramfs-debug-enable is provided by package helper-scripts.
#       chroot_run initramfs-debug-enable
#    fi

   true "INFO: Prevent Secure Boot MOK keys from being generated. This avoids a situation where all users use the same MOK keys."
   $SUDO_TO_ROOT -- mkdir --parents -- "$(dirname "$target_dkms_suppress_mok_snippet")"
   $SUDO_TO_ROOT -- cp --verbose -- "$source_dkms_suppress_mok_snippet" "$target_dkms_suppress_mok_snippet"

   if printf "%s\n" "${BUILD_INITRAMFS_PKGS}" | grep "dracut" >/dev/null ; then
      true "INFO: Prevent running dracut needlessly. Will be run at the end. This is only to speed up the build."
      $SUDO_TO_ROOT -- mkdir --parents -- "$(dirname "$target_dracut_disable_config_snippet")"
      $SUDO_TO_ROOT -- cp --verbose -- "$source_dracut_disable_config_snippet" "$target_dracut_disable_config_snippet"
   fi

   ## Install legacy-dist earlier so debconf questions are answered before
   ## meta package installation.
   pkg-install legacy-dist

   if [ "${dist_build_type_short:-}" = "kicksecure" ]; then
      pkg-install kicksecure-packages-dependencies-pre
   elif [ "${dist_build_type_short:-}" = "whonix" ]; then
      if [ "${dist_build_type_long:-}" = "gateway" ];then
         pkg-install whonix-gateway-packages-dependencies-pre
      elif [ "${dist_build_type_long:-}" = "workstation" ];then
         pkg-install whonix-workstation-packages-dependencies-pre
      else
         true "${bold}${cyan}INFO: No packages-dependencies-pre package, ok.${reset}"
      fi
   else
      error "ERROR: Invalid dist_build_type_short '$dist_build_type_short'. Please report this bug!"
   fi

   ## Install mate-polkit before we install anything GUI-related, since
   ## otherwise ukui-polkit will attempt to insert itself into the image.
   ##
   ## Not using lxqt-policykit because it segfaults if pam-info echos any
   ## messages, and sometimes pops up swarms of windows with data from
   ## pam-info.
   if [ "${dist_build_desktop:-}" = 'LXQt' ]; then
     pkg-install mate-polkit
   fi

   "$dist_source_help_steps_folder/unchroot-raw" "$@"

   ## }}

   "$dist_source_help_steps_folder/chroot-raw" "$@"
   "$dist_source_help_steps_folder/create-local-temp-apt-repo" "$@"

   ## Reading Debian apt repository and local repository containing derivative packages.
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts update

   ## Debugging.
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts clean
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts autoclean
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts --fix-broken --yes install
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts --fix-missing --yes install
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts --fix-broken --fix-missing --yes install
   chroot_run dpkg --configure -a
   chroot_run dpkg --audit

   ## XXX: once installed a newer kernel, which then messed up /boot/grub/grub.cfg to then include root=/dev/mapper which will fail to boot.
   chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts --yes dist-upgrade

   ## Debugging.
   chroot_run apt-cache "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts show nano || true
   chroot_run apt-cache "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts show helper-scripts || true

   if [ "${dist_build_script_skip_package_install:-}" = "" ]; then
      true "${bold}${cyan}INFO ${BASH_SOURCE[0]}: Variable dist_build_script_skip_package_install is empty. \
No packages will be excluded from installation. This information is relevant for builders using custom configurations, \
skipping packages such as Terminal-Only.${reset}"
   else
      true "${bold}${cyan}INFO ${BASH_SOURCE[0]}: List of packages to be excluded from installation. This information is relevant for \
builders using custom configurations, skipping packages such as Terminal-Only. dist_build_script_skip_package_install: \
$dist_build_script_skip_package_install${reset}"
   fi

   if [ "${dist_build_install_to_root:-}" = "true" ]; then
      true "${bold}${cyan}INFO: dist_build_install_to_root is set to true. Skipping kernel installation (one should already be installed), ok.${reset}"
   else
      ## Need to install initramfs tool before kernel. Otherwise kernel would pull Debian's default which is initramfs-tools.
      if [ "${BUILD_INITRAMFS_PKGS:-}" = "none" ]; then
         true "${bold}${cyan}INFO: BUILD_INITRAMFS_PKGS: ${BUILD_INITRAMFS_PKGS} - Skipping initramfs tool installation.${reset}"
      else
         true "${bold}${cyan}INFO: dist_build_install_to_root is not set to true. Add initramfs tool to installation list...${reset}"
         true "${bold}${cyan}INFO: BUILD_INITRAMFS_PKGS: ${BUILD_INITRAMFS_PKGS}${reset}"
         local build_header
         for build_initramfs in $BUILD_INITRAMFS_PKGS; do
            pkg-add-to-install-list "$build_initramfs"
         done
      fi
      if [ "${BUILD_KERNEL_PKGS:-}" = "none" ]; then
         true "${bold}${cyan}INFO: BUILD_KERNEL_PKGS: ${BUILD_KERNEL_PKGS} - Skipping kernel installation.${reset}"
      else
         true "${bold}${cyan}INFO: dist_build_install_to_root is not set to true. Add to kernel image to installation list...${reset}"
         true "${bold}${cyan}INFO: BUILD_KERNEL_PKGS: ${BUILD_KERNEL_PKGS}${reset}"
         local build_kernel
         for build_kernel in $BUILD_KERNEL_PKGS; do
            pkg-add-to-install-list "$build_kernel"
         done
      fi
      if [ "${BUILD_HEADER_PKGS:-}" = "none" ]; then
         true "${bold}${cyan}INFO: BUILD_HEADER_PKGS: ${BUILD_HEADER_PKGS} - Skipping kernel header installation.${reset}"
      else
         true "${bold}${cyan}INFO: dist_build_install_to_root is not set to true. Add kernel header to installation list...${reset}"
         true "${bold}${cyan}INFO: BUILD_HEADER_PKGS: ${BUILD_HEADER_PKGS}${reset}"
         local build_header
         for build_header in $BUILD_HEADER_PKGS; do
            pkg-add-to-install-list "$build_header"
         done
      fi
   fi

   #if [ "${dist_build_flavor:-}" != "whonix-gateway-rpi" ] && [ "${dist_build_target_arch:-}" = "arm64" ]; then
      #pkg-add-to-install-list grub2-common
      #pkg-add-to-install-list grub-efi-arm64
   #fi

   ## Weak recommended packages. No other package depends on it. Can be
   ## easily uninstalled. For better usability.
   if [ "${dist_build_virtualbox:-}" = "true" ]; then
      ## See also build-steps.d/*_create-debian-packages function
      ## download_virtualbox_packages_from_debian_sid

      pkg-add-to-install-list virtualbox-guest-utils
      pkg-add-to-install-list virtualbox-guest-x11

      ## https://packages.debian.org/virtualbox-guest-additions-iso is available for architecture "all".
      ##
      ## Not required but keeping in case above packages become unavailable.
      ## Allows for higher flexibility if later switches from/to
      ## virtualbox.org repository or Debian fasttrack repository are required.
      ##
      ## https://packages.debian.org/sid/virtualbox-guest-additions-iso
      ##
      ## provides:
      ## /usr/share/virtualbox/VBoxGuestAdditions.iso
      pkg-add-to-install-list virtualbox-guest-additions-iso
   else
      true "${cyan}INFO: skipping installation of weak recommended guest additions, because not using --target virtualbox, ok.${reset}"
   fi

   true "VMNAME: $VMNAME"
   true "dist_build_gui: $dist_build_gui"
   true "dist_build_type_short: $dist_build_type_short"
   true "dist_build_type_long: $dist_build_type_long"

   if [ "${dist_build_gui:-}" = "true" ]; then
      true "dist_build_gui detected."
      if [ "${dist_build_type_short:-}" = "kicksecure" ]; then
         true "dist_build_type_short kicksecure detected."
         pkg-add-to-install-list user-sysmaint-split
      fi
      if [ "${dist_build_type_long:-}" = "workstation" ];then
         true "dist_build_type_long workstation detected."
         pkg-add-to-install-list user-sysmaint-split
      fi
      if [ "${dist_build_type_long:-}" = "gateway" ];then
         true "dist_build_type_long workstation detected."
         pkg-add-to-install-list user-sysmaint-split
      fi
      ## There are other build types, such as `whonix-host` and
      ## `custom-workstation`, where user-sysmaint-split may not be desirable.
      ## This is why we explicitly check for supported build types rather than
      ## always marking user-sysmaint-split for installation here.
   fi

   ## Executing pkg-list-install already this this point for the first time to
   ## make sure linux kernel(s) and linux header(s) as well as
   ## virtualbox-guest-additions-iso get installed now so it is available
   ## during later package vm-config-dist postinst (vbox-guest-installer by
   ## derivative maintainers) without having to add `Depends:` to
   ## vm-config-dist.
   pkg-list-install

   local efi_weak_recommended_packages_list
   ## Availability (and usefulness) is architecture specific.
   efi_weak_recommended_packages_list="sbsigntool efibootmgr keyutils shim-signed-common efivar fwupd fwupd-signed"

   for iso_weak_recommended_packages_item in $efi_weak_recommended_packages_list ; do
      ## Test if available. Might be unavailable (and unneeded) on some architectures.
      if chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" $apt_sourcelist_empty $apt_sourceparts $apt_unattended_opts --yes --dry-run install "$iso_weak_recommended_packages_item" &>/dev/null ; then
         true "INFO: Yes, adding weak recommended package to the installation list: $iso_weak_recommended_packages_item"
         pkg-add-to-install-list "$iso_weak_recommended_packages_item"
      else
         true "INFO: No, not adding weak recommended package to the installation list: $iso_weak_recommended_packages_item"
      fi
   done

   if [ "${dist_build_raw:-}" = "true" ]; then
      ## XXX: Superfluous for non-libvirt raw image builds.
      pkg-add-to-install-list spice-vdagent
      #pkg-add-to-install-list serial-console-enable
   elif [ "${dist_build_qcow2:-}" = "true" ]; then
      pkg-add-to-install-list spice-vdagent
      #pkg-add-to-install-list serial-console-enable
   else
      true "${cyan}INFO: skipping installation of weak recommended guest additions packages spice-vdagent because not using --target qcow2, ok.${reset}"
   fi

   ## kicksecure-network-conf{,-gui} cannot be listed in metapackages, as it
   ## can break networking in some situations.
   if [ "${dist_build_type_short:-}" = "kicksecure" ]; then
      pkg-add-to-install-list kicksecure-network-conf
      if [ "${dist_build_gui:-}" = 'true' ]; then
         pkg-add-to-install-list kicksecure-network-conf-gui
      fi
   fi

   ## tirdad cannot be listed in metapackages, as it will result in the
   ## installation of a kernel, which is undesirable in containers.
   pkg-add-to-install-list tirdad

   ## Debugging.
   #pkg-add-to-install-list debug-misc

   if [ "${flavor_meta_packages_to_install:-}" = "none" ] || [ "${flavor_meta_packages_to_install:-}" = "" ] || [ "${flavor_meta_packages_to_install:-}" = " " ]; then
      true "${cyan}INFO: variable flavor_meta_packages_to_install is set to '$flavor_meta_packages_to_install', skipping.${reset}"
   else
      for flavor_meta_package_item in $flavor_meta_packages_to_install ; do
         true "${cyan}INFO: flavor_meta_packages_to_install: '$flavor_meta_packages_to_install'${reset}"
         pkg-add-to-install-list "$flavor_meta_package_item"
      done
   fi

   if [ "${install_package_list:-}" = "none" ] || [ "${install_package_list:-}" = "" ] || [ "${install_package_list:-}" = " " ]; then
      true "${cyan}INFO: variable install_package_list (custom additional packages list) is set to '$install_package_list', skipping, ok.${reset}"
   else
      for install_package_item in $install_package_list ; do
         true "${cyan}INFO: install_package_item: '$install_package_item'${reset}"
         pkg-add-to-install-list "$install_package_item"
      done
   fi

   ## NOTE: firmware:
   ##       This script 'build-steps.d/*_install-packages' is not used for ISO / host builds.
   ##       Therefore no need
   #pkg-add-to-install-list "$meta_package_firmware"

   pkg-list-install

   ## repository-dist-initializer
   ## requires: variable CHROOT_FOLDER
   repository_dist_initializer_setup

   ## This is not a chroot-script, because initrd must be re-generated after running this tool.
   ## To avoid generating initrd here and during run of chroot-scripts, run initrd generation
   ## only one time and only here.
   ## Related to systemd-repart configuration.
   ## Hard dependency on package 'initializer-dist'.
   chroot_run /usr/libexec/initializer-dist/75_growfs

   ## Generate GRUB keyboard layouts.
   ## 'set-grub-keymap' is provided by package 'helper-scripts'.
   ## Use '--no-live-changes' because GRUB is handled by 'build-steps.d/*_install-packages'.
   ## NOTE: 'build-steps.d/*_create-lb-iso' also uses 'set-grub-keymap'. Should use the same/similar options.
   chroot_run set-grub-keymap --build-all --no-live-changes

   if printf "%s\n" "${BUILD_INITRAMFS_PKGS}" | grep -q dracut ; then
      ## dracut is automatically run during above package installation through the usual
      ## Debian package triggers might break the boot process. Re-running dracut with
      ## the correct command line parameters is required in order to fix that.

      $SUDO_TO_ROOT safe-rm --force --verbose -- "$target_dracut_disable_config_snippet"

      ## Debugging.
      chroot_run cat -- /etc/fstab || true
      chroot_run dracut --print-cmdline --fstab || true

      ## For documentation on how to install dracut in a chroot, see:
      ## https://github.com/dracutdevs/dracut/issues/1596

      KERNELIMG="$(chroot_run ls -1 -- "/boot/")"
      KERNELIMG="$(printf "%s\n" "$KERNELIMG" | grep "vmlinuz")"
      KERNELIMG="$(printf "%s\n" "$KERNELIMG" | sed -n '1p')"
      ## example KERNELIMG:
      ## vmlinuz-6.1.0-9-amd64

      KERNELVER="${KERNELIMG#vmlinuz-}"
      ## example KERNELVER:
      ## 6.1.0-9-amd64

      ## Add dracut support to grml-debootstrap:
      ## https://github.com/grml/grml-debootstrap/pull/196
      ##
      ## INITRD_GENERATOR_OPTS is set in help-steps/variables.
      chroot_run \
            dracut \
               --no-hostonly \
               --kver "$KERNELVER" \
               --force \
               --reproducible \
               --verbose \
               $INITRD_GENERATOR_OPTS
   fi

   ## Undo disabling of DKMS MOK generation, so MOK keys will be generated for
   ## the end user.
   $SUDO_TO_ROOT safe-rm --force --verbose -- "$target_dkms_suppress_mok_snippet"

   ## grub-mkconfig does this.
   local grub_probe_main_device_output grub_probe_boot_device_output grub_probe_target_device
   grub_probe_main_device_output="$(chroot_run grub-probe --target=device /)" || true
   grub_probe_boot_device_output="$(chroot_run grub-probe --target=device /boot)" || true

   ## Debugging.
   printf '%s\n' "${grub_probe_main_device_output}"
   printf '%s\n' "${grub_probe_boot_device_output}"

   ## Sometimes this outputs:
   ## /dev/mapper/loop0p2
   ## Which results in:
   ## GRUB_DEVICE=/dev/mapper/loop0p2
   ## which results in:
   ## root=/dev/mapper/loop0p2
   ## which then results in an unbootable system.

   ## TODO: comment in
   #chroot_run update-grub

   ## TODO: comment out
   ## Debugging.
   ## update-grub is a wrapper that essentially runs:
   ## grub-mkconfig -o /boot/grub/grub.cfg
   ## Run grub-mkconfig with sh xtrace enabled for debug output.
   ## Full path to /usr/sbin/grub-mkconfig required because using sh -xv.
   chroot_run sh -xv /usr/sbin/grub-mkconfig -o /boot/grub/grub.cfg
   ## TODO: enable verbose output during execution of /etc/grub.d/10_linux somehow.
   ##       It is being executed by grub-mkconfig. Needed to investigate where the
   ##       root=/dev/mapper issue is coming from

   ## Debugging.
   true "/boot/grub/grub.cfg root= :"
   chroot_run cat -- /boot/grub/grub.cfg | grep --color "root=" || true

   ## Sanity test.
   if chroot_run cat -- /boot/grub/grub.cfg | grep --quiet "root=/dev/mapper" ; then
      ## Attempt to fix /dev/mapper issue.
      if [ "${grub_probe_main_device_output}" = "${grub_probe_boot_device_output}" ]; then
        grub_probe_target_device="${grub_probe_main_device_output}"
      else
        grub_probe_target_device="${grub_probe_boot_device_output}"
      fi
      if ! [[ "${grub_probe_target_device}" =~ /dev/mapper ]]; then
        error '/boot/grub/grub.cfg contains root=/dev/mapper and cannot find proper boot device - image most likely unbootable!'
      fi
      grub_probe_target_device="$(cut -d'/' -f4 <<< "${grub_probe_target_device}")"
      grub_probe_target_device="$(lsblk -rno NAME,UUID | grep --fixed-strings -- "${grub_probe_target_device}")"
      grub_probe_target_device="$(cut -d' ' -f2 <<< "${grub_probe_target_device}")"
      grub_probe_target_device="$(head -n1 <<< "${grub_probe_target_device}")"
      if [ -z "${grub_probe_target_device}" ]; then
        error '/boot/grub/grub.cfg contains root=/dev/mapper and cannot find boot device UUID - image most likely unbootable!'
      fi
      $SUDO_TO_ROOT sed -i "s;root=[^ ]\\+;root=UUID=${grub_probe_target_device};" $CHROOT_FOLDER/boot/grub/grub.cfg
   fi

   "$dist_source_help_steps_folder/remove-local-temp-apt-repo" "$@"
   "$dist_source_help_steps_folder/unprevent-daemons-from-starting" "$@"

   ## Forget about local repository containing derivative packages.
   #chroot_run apt-get-noninteractive --no-download --list-cleanup update

   chroot_run sync
   sync

   "$dist_source_help_steps_folder/unchroot-raw" "$@"
   "$dist_source_help_steps_folder/unmount-raw" "$@"

   sync
}

main() {
   if [ "${build_dry_run:-}" = "true" ]; then
      true "${bold}${cyan}INFO: dry-run, skipping ${BASH_SOURCE[0]}. ${reset}"
      return 0
   fi

   if [ "${dist_build_iso:-}" = "true" ]; then
       ## NOTE: ISO builds do not use 'build-steps.d/*_install-packages' to install packages.
       true "${green}INFO: Skipping ${BASH_SOURCE[0]}, because dist_build_iso is set to true.${reset}"
       return 0
   fi

   if [ "${dist_build_flavor:-}" = "whonix-custom-workstation" ]; then
      true "${cyan}INFO: Skipping installing packages for $VMNAME.${reset}"
   else
      install-packages "$@"
   fi
}

main "$@"
