#!/bin/bash

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

## Static-analysis waiver: intentional word-splitting of apt option / package
## variables ($apt_sourceparts, $apt_unattended_opts, $pkg_install_list, ...);
## quoting them would pass a single argument and break the call.
# shellcheck disable=SC2086

set -x
set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose
export LC_ALL=C

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"
   ## retry_run (help-steps/pre) retries transient upstream fetch failures
   ## (a derivative repo mid-publish 404, a dropped connection, a 5xx, a
   ## hash-sum mismatch); a non-transient failure (unmet dependencies) is not
   ## a fetch error and returns immediately.
   retry_run --tries 4 --delay 15 -- \
      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

   ## retry_run already retried transient failures; a failure here is
   ## non-transient or persisted after all retries.
   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}"

   ## Re-run the install once more, capturing its combined output into
   ## $apt_get_output, which apt_get_parse_unmet_dependency parses to surface
   ## unmet dependencies. (retry_run streams and discards its own capture, so
   ## the variable must be populated here for the diagnosis below.)
   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: Last 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-remove() {
   exception_handler_setup "exception_handler_unchroot_unmount" ERR INT TERM

   true "${cyan}INFO: Removing '$*'... This may take a while...${reset}"

   local apt_get_exit_code="0"
   chroot_run \
      apt-get-noninteractive \
         "${DIST_APTGETOPT[@]}" \
            ${apt_sourcelist_empty} \
            ${apt_sourceparts} \
            ${apt_unattended_opts} \
            --yes \
            remove \
            "$@" \
            || { apt_get_exit_code="$?" ; true; };

   chroot_run sync
   ${SUDO_TO_ROOT} sync

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

   true "${bold}${red}ERROR: Failed to remove '$*'!${reset}"
   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
}

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

   local pkg_remove_item
   pkg_remove_item="$1"
   if [ "${pkg_remove_item:-}" = 'none' ]; then
      true "${bold}${byan}INFO: Skipping removing package package ${pkg_remove_item} (none).${reset}"
      return 0
   fi

   if [ "${pkg_install_debug:-}" = 'true' ]; then
      pkg-remove "${pkg_remove_item}"
   else
      pkg_remove_list+=" ${pkg_remove_item} "
   fi
}

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

   if [ "${pkg_remove_list:-}" = "" ]; then
      true "INFO: pkg_remove_list still empty, ok."
      return 0
   else
      pkg-remove ${pkg_remove_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
   ## We use 'install' rather than 'cp' to keep the host's umask applied to
   ## files in Git from applying to the image being built.
   ${SUDO_TO_ROOT} install --mode=0644 -- "${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} install --mode=0755 -- "${source_code_folder_dist}/packages/kicksecure/helper-scripts/usr/bin/apt-get-noninteractive" "${CHROOT_FOLDER}/usr/bin/apt-get-noninteractive"

   ## Some helper scripts are needed for apt-get-noninteractive to work.
   ${SUDO_TO_ROOT} mkdir --parents -- "${CHROOT_FOLDER}/usr/libexec/helper-scripts"
   for dist_apt_wrapper_lib in strings.bsh wc-test.sh check_runtime.bsh; do
      ${SUDO_TO_ROOT} install --mode=0755 -- "${source_code_folder_dist}/packages/kicksecure/helper-scripts/usr/libexec/helper-scripts/${dist_apt_wrapper_lib}" "${CHROOT_FOLDER}/usr/libexec/helper-scripts/${dist_apt_wrapper_lib}"
   done

   "${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" "$@"

   retry_run --tries 4 --delay 15 -- 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.
   retry_run --tries 4 --delay 15 -- 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
   retry_run --tries 4 --delay 15 -- chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" ${apt_sourcelist_empty} ${apt_sourceparts} ${apt_unattended_opts} --fix-broken --yes install
   retry_run --tries 4 --delay 15 -- chroot_run apt-get-noninteractive "${DIST_APTGETOPT[@]}" ${apt_sourcelist_empty} ${apt_sourceparts} ${apt_unattended_opts} --fix-missing --yes install
   retry_run --tries 4 --delay 15 -- 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.
   retry_run --tries 4 --delay 15 -- 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.
   ##
   ## Out-commented because metapackage dependencies are currently being used
   ## for virtualbox-guest-additions-iso, and virtualbox-guest-* is no longer
   ## being used by us.
   #if [ "${dist_build_virtualbox:-}" = "true" ] \
      ## || [ "${dist_build_iso:-}" = "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". It 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 efi_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 "${efi_weak_recommended_packages_item}" &>/dev/null ; then
         true "INFO: Yes, adding weak recommended package to the installation list: ${efi_weak_recommended_packages_item}"
         pkg-add-to-install-list "${efi_weak_recommended_packages_item}"
      else
         true "INFO: No, not adding weak recommended package to the installation list: ${efi_weak_recommended_packages_item}"
      fi
   done

   ## '--serial-console-enable true': install the serial-console-enable package
   ## so the image can be driven headless over a serial line.
   if [ "${dist_build_serial_console_enable:-}" = "true" ]; then
      true "${cyan}INFO: --serial-console-enable true: adding serial-console-enable.${reset}"
      pkg-add-to-install-list serial-console-enable
   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

   ## firmware-nonfreedom{,-noarch} cannot be listed in metapackages, as it is
   ## in the non-free pocket.
   if [ "${dist_build_type:-}" = 'host' ] && [ "${build_freedom_only:-}" = 'false' ]; then
      pkg-add-to-install-list firmware-nonfreedom-noarch
      if [ "${dist_build_target_arch:-}" = 'amd64' ]; then
         pkg-add-to-install-list firmware-nonfreedom
      fi
   fi

   ## 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

   if [ "${dist_build_iso:-}" = "true" ]; then
      ## Add packages that are necessary for the ISO to function.
      pkg-add-to-install-list calamares
      pkg-add-to-install-list calamares-settings-debian
      pkg-add-to-install-list live-config-dist
      pkg-add-to-install-list dracut-live
      pkg-add-to-install-list isomd5sum
      #pkg-add-to-install-list grub

      case "${dist_build_target_arch:-}" in
         amd64)
            pkg-add-to-install-list grub-pc-bin
            pkg-add-to-install-list grub-efi-amd64-bin
            pkg-add-to-install-list grub-efi-ia32-bin
            pkg-add-to-install-list grub-efi-amd64-signed
            pkg-add-to-install-list shim-signed
            pkg-add-to-remove-list grub-cloud-amd64
            ;;
         arm64)
            pkg-add-to-install-list grub-efi-arm64-bin
            pkg-add-to-install-list grub-efi-arm64-signed
            pkg-add-to-install-list shim-signed
            pkg-add-to-remove-list grub-cloud-arm64
            ;;
      esac
   fi

   pkg-list-install
   pkg-list-remove

   ## 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'.
   chroot_run set-grub-keymap --build-all --no-live-changes

   if printf "%s\n" "${BUILD_INITRAMFS_PKGS}" | grep dracut >/dev/null 2>&1 ; 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

      KERNELVER="$(newest-kernel-version "$(chroot_run ls -1 -- "/boot/")")"
      ## 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.

   ## '--smbios-reader true': let vm-config-dist's etc/grub.d/01_smbios-reader and
   ## etc/default/grub.d/99_smbios-cmdline.cfg emit the SMBIOS cmdline reader.
   if [ "${dist_build_smbios_reader:-}" = "true" ]; then
      true "${cyan}INFO: --smbios-reader true: enabling the GRUB SMBIOS cmdline reader.${reset}"
      ${SUDO_TO_ROOT} install --mode=0644 -- "${source_code_folder_dist}/packages/kicksecure/vm-config-dist/usr/share/vm-config-dist/50_dm-smbios-reader.cfg" "${CHROOT_FOLDER}/etc/default/grub.d/50_dm-smbios-reader.cfg"
   else
      ## If the option wasn't passed, delete the file to make sure it doesn't
      ## pollute a build reusing a cached base image. For performance reasons,
      ## do not refactor this into an unconditional remove.
      ${SUDO_TO_ROOT} safe-rm --force -- "${CHROOT_FOLDER}/etc/default/grub.d/50_dm-smbios-reader.cfg"
   fi

   ## 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

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

   ## Sanity test.
   if chroot_run grep "root=/dev/mapper" /boot/grub/grub.cfg >/dev/null 2>&1; then
      ## Attempt to fix /dev/mapper issue.
      if [ -z "${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="$(resolve-partition-uuid "${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

   ## Prepare the on-ISO apt repo if appropriate. Packages have to be
   ## downloaded in the chroot to ensure they are the right packages.
   if [ "${dist_build_iso:-}" = 'true' ]; then
      safe-rm --recursive --force -- "${binary_iso_pkg_repo_dir}"
      mkdir -p "${binary_iso_pkg_repo_dir}"
      ${SUDO_TO_ROOT} mkdir -p "${CHROOT_FOLDER}/tmp_iso_pkg_repo_dir"
      for iso_pkg_repo_seed_item in ${iso_pkg_repo_seed_list} ; do
         chroot_run bash -c "cd /tmp_iso_pkg_repo_dir; apt download ${iso_pkg_repo_seed_item}"
      done
      ${SUDO_TO_ROOT} mv -- "${CHROOT_FOLDER}/tmp_iso_pkg_repo_dir"/* "${binary_iso_pkg_repo_dir}/"
      ${SUDO_TO_ROOT} rmdir "${CHROOT_FOLDER}/tmp_iso_pkg_repo_dir"
      ## Must be in ${binary_iso_pkg_repo_dir} when creating the Packages file,
      ## otherwise the file will contain absolute paths that are no longer
      ## valid in the live session.
      pushd "${binary_iso_pkg_repo_dir}"
      dpkg-scanpackages . /dev/null | tee -- ./Packages >/dev/null
      popd
   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_flavor:-}" = "whonix-custom-workstation" ]; then
      true "${cyan}INFO: Skipping installing packages for ${VMNAME}.${reset}"
   else
      install-packages "$@"
   fi
}

main "$@"
