Code:
lib/utils
#!/usr/bin/env bash
die() {
    echo "Error: $1. Exiting..." 1>&2
    exit 1
}
require_binaries() {
    local binaries=( "$@" )
    for binary in "${binaries[@]}"; do
        if ! command -v "${binary}" &> /dev/null; then
            die "${binary} binary does not exist"
        fi
    done
}
is_mounted() {  # Copied from https://www.baeldung.com/linux/bash-is-directory-mounted
    mount | awk -v DIR="$1" '{if ($3 == DIR) { exit 0}} ENDFILE{exit -1}'
}
insist_root() {
    if [ "$EUID" -ne 0 ]; then
        echo "Please run as root"
        exit 1
    fi
}
array_contains_element() {
    local array=$1
    local str=$2
    for elem in "${array[@]}"; do
        if [[ "${elem}" == "${str}" ]]; then
            return 0
        fi
    done
    return 1
}
read_password() {
    local password confirm_password
    until
        stty -echo
        read -r -p "Password: " password
        echo
        read -r -p "Confirm password: " confirm_password
        echo
        stty echo
        [[ "${password}" = "${confirm_password}" ]]
    do
        echo "Error: passwords don't match." >&2
    done
    echo "${password}"
}
_another_instance() {
    echo "There is another instance running, exiting"
    exit 1
}
create_lock() {
    LOCKFILE=/var/lock/$(basename "$0")
    LOCKFD="100"
    eval "exec ${LOCKFD}>\"${LOCKFILE}\"" || exit 1
    flock -xn ${LOCKFD} || _another_instance
    trap 'flock -u ${LOCKFD}; flock -xn ${LOCKFD} && rm -f ${LOCKFILE}' EXIT
}
install_arch
#!/usr/bin/env bash
source ./lib/utils
insist_root
create_lock
require_binaries "pacman" "pacstrap" "genfstab" "arch-chroot"
ARCHZFS_REPO_KEY="DDF7DB817396A49B2A2723F7403BD972F75D9D76"
CWD="$(pwd)"
ARCH_AVAILABLE_PROFILES=( "grub" "uefi" "devel" "firmware" "parabola_firmware" "zfs" "kernel_headers" "python"
                          "zfs_force_dkms" "!dkms" "genfstab" "genfstab_uuid" "genfstab_label" "zfs_snapshot_install"
                          "swap_encrypt" "raid" )
ARCH_ENABLED_PROFILES=${ARCH_ENABLED_PROFILES:-"grub uefi firmware genfstab genfstab_uuid"}
usage() {
    cat <<EOF
$0 - Install Arch Linux base system
Usage:
    $0 [ options ]
    $0 --help
Available profiles:
    ${ARCH_AVAILABLE_PROFILES[*]}
Enabled profiles by default:
    ${ARCH_ENABLED_PROFILES[*]}
Options:
    -m|--mnt value - rootfs mount path, required
    -u|--user value - target user name, required (default: user)
    -p|--profiles "quoted list of values" - instructs script how to configure Arch 
    -e|--extra-software "quoted list of additional software to install"
    -t|--timezone "value" - target system timezone (default: UTC)
    --hwclock-timezone (localtime|UTC) - hardware clock timezone (default: UTC)
    -l|--enabled-locales "quoted list of values" - locales to enable in /etc/locale.gen (default: "en_US.UTF-8")
    -L|--locale value - default system locale (default: en_US.UTF-8)
    -H|--hostname value - system hostname (default: host)
    -D|--domain value - system domain (default: localdomain)
    --bootstrap-opts "quotes list of values" - bootstrap options used by initial call of pacstrap (default: -K)
    -k|--keymap - system keymap (default: en)
    -K|--kernels - system kernels (default: linux)
    --kernels-headers "quoted list of values" - system kernels' headers, not required
    -b|--boot-mode - boot mode (default: uefi)
    --efi-bootloader-id value - uefi bootloade id (default: arch)
    -B|--boot-devices "quoted list of values" - boot devices used by GRUB, required
    --initrd-modules "quoted list of modules" - initcpio modules
    --initrd-binaries "quoted list of binaries" - inicpio binaries
    --initrd-files "quoted list of files" - initcpio files
    --initrd-hooks "quoted list of hooks" - inicpio hooks
    --initrd-compression value - initcpio compression algorithm
    --initrd-compression-opts "quoted list of opts" - inicpio compression options
    --initrd-decompress value - specifies whether kernel modules should be decompressed during initramfs creation (default: no)
    --initrd-custom-hooks-files "quoted list of files" - initcpio custom hooks files
    --initrd-custom-install-files "quoted list of files" - initcpio custom hook install files
    --grub-boot-dir value - grub boot directory, only needed in uefi installation (default: /boot/efi/arch)
    --grub-efi-dir value - grub efi directory, only needed in uefi installation (default: /boot/efi)
    --zfs-boot-pool|--zfs-bpool - zfs boot pool (default: bpool)
    --zfs-root-pool|--zfs-rpool - zfs root pool (default: rpool)
    --zfs-home-dataset - zfs home dataset (default: rpool/home/${ARCH_USER})
    --zfs-snapshot-name value - specifies initial installation snapshot name (default: install)
    --zfs-module-pkgname value - specifies zfs kernel module package name if dkms is globally disabled, not required
    --swap-cryptdev value - specifies swap crypt device when swap_encrypt profile is enabled (default: none)
    --config-dir - config directory (default: ${CWD}/install_arch.d)
    -c|--config - script config file, required
    --apply - tells script that changes should be applied, use with caution
    -h|--help - displays this help
EOF
    exit 0
}
[ $# -eq 0 ] && usage
# parse script arguments
while [ "$#" -gt 0 ]; do
    case $1 in
        -m|--mnt)
            ARCH_MNT_PATH="$2"
            shift
            shift
            ;;
        -u|--user)
            ARCH_USER="$2"
            shift
            shift
            ;;
        -p|--profiles)
            ARCH_ENABLED_PROFILES="$2"
            shift
            shift
            ;;
        -e|--extra-software)
            ARCH_EXTRA_SOFTWARE="$2"
            shift
            shift
            ;;
        -t|--timezone)
            ARCH_TIMEZONE="$2"
            shift
            shift
            ;;
        --hwclock-timezone)
            ARCH_HWCLOCK_TIMEZONE="$2"
            shift
            shift
            ;;
        -l|--enabled-locales)
            ARCH_ENABLED_LOCALES="$2"
            shift
            shift
            ;;
        -L|--locale)
            ARCH_LOCALE="$2"
            shift
            shift
            ;;
        -H|--hostname)
            ARCH_HOSTNAME="$2"
            shift
            shift
            ;;
        -D|--domain)
            ARCH_DOMAIN="$2"
            shift
            shift
            ;;
        --bootstrap-opts)
            ARCH_BOOTSTRAP_OPTS="$2"
            shift
            shift
            ;;
        -k|--keymap)
            ARCH_KEYMAP="$2"
            shift
            shift
            ;;
        -K|--kernels)
            ARCH_KERNELS="$2"
            shift
            shift
            ;;
        --kernels-headers)
            ARCH_KERNELS_HEADERS="$2"
            shift
            shift
            ;;
        -b|--boot-mode)
            ARCH_BOOT_MODE="$2"
            shift
            shift
            ;;
        --efi-bootloader-id)
            ARCH_EFI_BOOTLOADER_ID="$2"
            shift
            shift
            ;;
        -B|--boot-devices)
            ARCH_BOOT_DEVICES="$2"
            shift
            shift
            ;;
        --initrd-modules)
            ARCH_INITRD_MODULES="$2"
            shift
            shift
            ;;
        --initrd-binaries)
            ARCH_INITRD_BINARIES="$2"
            shift
            shift
            ;;
        --initrd-files)
            ARCH_INITRD_FILES="$2"
            shift
            shift
            ;;
        --initrd-hooks)
            ARCH_INITRD_HOOKS="$2"
            shift
            shift
            ;;
        --initrd-compression)
            ARCH_INITRD_COMPRESSION="$2"
            shift
            shift
            ;;
        --initrd-compression-opts)
            ARCH_INITRD_COMPRESSION_OPTS="$2"
            shift
            shift
            ;;
        --initrd-modules-decompress)
            ARCH_INITRD_MODULES_DECOMPRESS="y"
            shift
            shift
            ;;
        --initrd-custom-hooks-files)
            ARCH_INITRD_CUSTOM_HOOKS_FILES="$2"
            shift
            shift
            ;;
        --initrd-custom-install-files)
            ARCH_INITRD_CUSTOM_INSTALL_FILES="$2"
            shift
            shift
            ;;
        --grub-boot-dir)
            ARCH_GRUB_BOOT_DIR="$2"
            shift
            shift
            ;;
        --grub-efi-dir)
            ARCH_GRUB_EFI_DIR="$2"
            shift
            shift
            ;;
        --zfs-boot-pool|--zfs-bpool)
            ARCH_ZFS_BOOT_POOL="$2"
            shift
            shift
            ;;
        --zfs-root-pool|--zfs-rpool)
            ARCH_ZFS_ROOT_POOL="$2"
            shift
            shift
            ;;
        --zfs-home-dataset)
            ARCH_ZFS_HOME_DATASET="$2"
            shift
            shift
            ;;
        --zfs-snapshot-name)
            ARCH_ZFS_SNAPSHOT_NAME="$2"
            shift
            shift
            ;;
        --zfs-module-pkgname)
            ARCH_ZFS_MODULE_PKGNAME="$2"
            shift
            shift
            ;;
        --swap-cryptdev)
            ARCH_SWAP_CRYPTDEV="$2"
            shift
            shift
            ;;
        --config-dir)
            CONFIG_DIR="$2"
            shift
            shift
            ;;
        -c|--config)
            CONFIG_FILE="$2"
            shift
            shift
            ;;
        --apply)
            APPLY="y"
            shift
            ;;
        -h|--help)
            usage
            ;;
        *)
            echo "Error: command '$1' not recognized."
            usage
            ;;
    esac
done
# shellcheck source=/dev/null
if [[ -f "${CONFIG_FILE}" ]]; then
    source "${CONFIG_FILE}"
else
    die "config file does not exist"
fi
# assign default values to variables
ARCH_MNT_PATH=${ARCH_MNT_PATH:-}
ARCH_USER=${ARCH_USER:-"user"}
ARCH_EXTRA_SOFTWARE=${ARCH_EXTRA_SOFTWARE:-}
ARCH_TIMEZONE=${ARCH_TIMEZONE:-"UTC"}
ARCH_HWCLOCK_TIMEZONE=${ARCH_HWCLOCK_TIMEZONE:-"UTC"}
ARCH_ENABLED_LOCALES=${ARCH_ENABLED_LOCALES:-"en_US.UTF-8 UTF-8"}
ARCH_LOCALE=${ARCH_LOCALE:-"en_US.UTF-8"}
ARCH_HOSTNAME=${ARCH_HOSTNAME:-"host"}
ARCH_DOMAIN=${ARCH_DOMAIN:-"localdomain"}
ARCH_BOOTSTRAP_OPTS=${ARCH_BOOTSTRAP_OPTS:-"-K"}
ARCH_KEYMAP=${ARCH_KEYMAP:-"en"}
ARCH_KERNELS=${ARCH_KERNELS:-"linux"}
ARCH_KERNELS_HEADERS=${ARCH_KERNELS_HEADERS:-}
ARCH_BOOT_MODE=${ARCH_BOOT_MODE:-"uefi"}
ARCH_EFI_BOOTLOADER_ID=${ARCH_EFI_BOOTLOADER_ID:-"arch"}
ARCH_BOOT_DEVICES=${ARCH_BOOT_DEVICES:-}
ARCH_INITRD_MODULES=${ARCH_INITRD_MODULES:-"ext4"}
ARCH_INITRD_BINARIES=${ARCH_INITRD_BINARIES:-}
ARCH_INITRD_FILES=${ARCH_INITRD_FILES:-}
ARCH_INITRD_HOOKS=${ARCH_INITRD_HOOKS:-"base udev autodetect modconf kms keyboard keymap consolefont block filesystems fsck"}
ARCH_INITRD_COMPRESSION=${ARCH_INITRD_COMPRESSION:-}
ARCH_INITRD_COMPRESSION_OPTS=${ARCH_INITRD_COMPRESSION_OPTS:-}
ARCH_INITRD_MODULES_DECOMPRESS=${ARCH_INITRD_MODULES_DECOMPRESS:-}
ARCH_INITRD_CUSTOM_HOOKS_FILES=${ARCH_INITRD_CUSTOM_HOOKS_FILES:-}
ARCH_INITRD_CUSTOM_INSTALL_FILES=${ARCH_INITRD_CUSTOM_INSTALL_FILES:-}
ARCH_GRUB_BOOT_DIR=${ARCH_GRUB_BOOT_DIR:-"/boot"}
ARCH_GRUB_EFI_DIR=${ARCH_GRUB_EFI_DIR:-"/boot/efi"}
ARCH_ZFS_BOOT_POOL=${ARCH_ZFS_BOOT_POOL-"bpool"}
ARCH_ZFS_ROOT_POOL=${ARCH_ZFS_ROOT_POOL:-"rpool"}
ARCH_ZFS_HOME_DATASET=${ARCH_ZFS_HOME_DATASET:-"${ARCH_ZFS_ROOT_POOL}/home/${ARCH_USER}"}
ARCH_ZFS_SNAPSHOT_NAME=${ARCH_ZFS_SNAPSHOT_NAME:-"install"}
ARCH_ZFS_MODULE_PKGNAME=${ARCH_ZFS_MODULE_PKGNAME:-}
ARCH_SWAP_CRYPTDEV=${ARCH_SWAP_CRYPTDEV:-}
ARCH_POSTINST_SERVICES=${ARCH_POSTINST_SERVICES:-}
ARCH_POSTINST_CMDS=${ARCH_POSTINST_CMDS:-}
ARCH_ROOT_PASSWD=${ARCH_ROOT_PASSWD:-"\$1\$HNeYMtER\$0VoteufFxJBfPM1YkkNpk."} # default password: changeme
ARCH_USER_PASSWD=${ARCH_USER_PASSWD:-"\$1\$1ajoaeQC\$lubTSvAeoWn8kNvXIPoKi/"} # default password: changeme
CONFIG_DIR=${CONFIG_DIR:-"${CWD}/install_arch.d"}
APPLY=${APPLY:-}
validate_args() {
    if [[ -n "${ARCH_MNT_PATH}" ]]; then
        if [[ ! -d "${ARCH_MNT_PATH}" ]]; then
            die "directory '${ARCH_MNT_PATH}' does not exist"
        else
            if ! is_mounted "${ARCH_MNT_PATH}"; then
                die "directory '${ARCH_MNT_PATH}' is not mounted"
            fi
        fi
    else
        die "mount path not specified"
    fi
    [[ -z "${ARCH_USER}" ]] && die "target user name is not specified"
    if [[ -n "${ARCH_ENABLED_PROFILES[*]}" ]]; then
        for profile in "${ARCH_ENABLED_PROFILES[@]}"; do
            if [[ ! ${ARCH_AVAILABLE_PROFILES[*]} =~ ${profile} ]]; then
                die "unknown profile '${profile}'"
            fi
        done
    else
        die "profile list is empty"
    fi
    [[ -z "${ARCH_TIMEZONE}" ]] && die "timezone value is empty"
    if [[ "${ARCH_HWCLOCK_TIMEZONE}" != "UTC" && "${ARCH_HWCLOCK_TIMEZONE}" != "localtime" ]]; then
        die "valid options for --hwclock-timezone are 'UTC' and 'localtime'"
    fi
    if [[ -z "${ARCH_LOCALE[*]}" ]] || [[ -z "${ARCH_ENABLED_LOCALES[*]}" ]]; then
        die "locale not specified"
    fi
    [[ -z "${ARCH_HOSTNAME}" ]] && die "hostname is empty"
    [[ -z "${ARCH_DOMAIN}" ]] && die "domain name is empty"
    [[ -z "${ARCH_KEYMAP}" ]] && die "keymap info isn't specified"
    [[ -z "${ARCH_KERNELS[*]}" ]] && die "kernel list is empty"
    if [[ -n "${ARCH_BOOT_MODE}" ]]; then
        case "${ARCH_BOOT_MODE}" in
            "uefi") ;;
            "legacy_bios") ;;
            *) die "boot mode must be 'uefi' or 'legacy_bios'" ;;
        esac
    else
        die "boot mode value is empty"
    fi
    for device in "${ARCH_BOOT_DEVICES[@]}"; do
        if [[ ! -b "${device}" ]]; then
            die "${device}: not a block device"
        fi
    done
}
is_profile_enabled() {
    for profile in "${ARCH_ENABLED_PROFILES[@]}"; do
        if [[ "${profile}" == "$1" ]]; then
            return 0
        fi
    done
    return 1
}
check_profiles_deps() {
    if ! is_profile_enabled "zfs"; then
        is_profile_enabled "zfs_force_dkms" && die "profile 'zfs_force_dkms' depends on the profile 'zfs'"
        is_profile_enabled "zfs_snapshot_install" && die "profile 'zfs_snapshot_install' depends on the profile 'zfs'"
    fi
    if ! is_profile_enabled "genfstab"; then
        is_profile_enabled "genfstab_uuid" && die "profile 'genfstab_uuid' depends on the profile 'genfstab'"
        is_profile_enabled "genfstab_label" && die "profile 'genfstab_label' depends on the profile 'genfstab'"
    fi
    if is_profile_enabled "zfs_force_dkms" && is_profile_enabled "!dkms"; then
        die "mutually exclusive profiles: 'zfs_force_dkms' and '!dkms'"
    fi
    if is_profile_enabled "kernel_headers" && ! is_profile_enabled "devel"; then
        die "profile 'kernel_headers' depends on the profile 'devel'"
    fi
    if is_profile_enabled "python" && ! is_profile_enabled "devel"; then
        die "profile 'python' depends on the profile 'devel'"
    fi
    if is_profile_enabled "genfstab_uuid" && ! is_profile_enabled "genfstab"; then
        die "profile 'genfstab_uuid' depends on the profile 'genfstab'"
    fi
    if is_profile_enabled "genfstab_label" && ! is_profile_enabled "genfstab"; then
        die "profile 'genfstab_label' depends on the profile 'genfstab'"
    fi
}
install_base_system() {
    echo "Installing base system"
    pacstrap "${ARCH_BOOTSTRAP_OPTS[*]}" "${ARCH_MNT_PATH}" base
}
generate_fstab() {
    local genfstab_args
    genfstab_args=""
    echo "Generating /etc/fstab"
    is_profile_enabled "uuid" && genfstab_args="-U"
    is_profile_enabled "label" && genfstab_args="-L"
    genfstab "${genfstab_args}" "${ARCH_MNT_PATH}" >> "${ARCH_MNT_PATH}"/etc/fstab
}
set_timezone() {
    echo "Setting timezone"
    arch-chroot "${ARCH_MNT_PATH}" ln -sf /usr/share/zoneinfo/"${ARCH_TIMEZONE}" /etc/localtime
    case "${ARCH_HWCLOCK_TIMEZONE}" in
        "UTC")
            arch-chroot "${ARCH_MNT_PATH}" hwclock --systohc --utc
            ;;
        "localtime")
            arch-chroot "${ARCH_MNT_PATH}" hwclock --systohc --localtime
            ;;
    esac
}
set_locale() {
    local IFS
    IFS=":"
    echo "Setting locale"
    for locale in "${ARCH_ENABLED_LOCALES[@]}"; do
        sed -i "s/#${locale}/${locale}/g" "${ARCH_MNT_PATH}"/etc/locale.gen
    done
    echo "LANG=${ARCH_LOCALE}" > "${ARCH_MNT_PATH}"/etc/locale.conf
    arch-chroot "${ARCH_MNT_PATH}" locale-gen
}
set_keymap() {
    echo "Setting keymap"
    echo "KEYMAP=${ARCH_KEYMAP}" > "${ARCH_MNT_PATH}"/etc/vconsole.conf
}
set_hostname() {
    echo "Setting hostname"
    echo "${ARCH_HOSTNAME}" > "${ARCH_MNT_PATH}"/etc/hostname
    cat <<EOF > "${ARCH_MNT_PATH}"/etc/hosts
# Static table lookup for hostnames.
# See hosts(5) for details.
# The following lines are desirable for IPv4 capable hosts
127.0.0.1               localhost
# 127.0.1.1 is often used for the FQDN of the machine
127.0.1.1               ${ARCH_HOSTNAME}.${ARCH_DOMAIN} ${ARCH_HOSTNAME}
# The following lines are desirable for IPv6 capable hosts
::1                     localhost ip6-localhost ip6-loopback
ff02::1                 ip6-allnodes
ff02::2                 ip6-allrouters
EOF
}
enable_dhcpcd() {
    echo "Installing dhcpcd"
    pacstrap "${ARCH_MNT_PATH}" dhcpcd
    echo "Enabling dhcpcd"
    arch-chroot "${ARCH_MNT_PATH}" systemctl enable dhcpcd
}
install_devel_pkgs() {
    echo "Installing base development packages"
    pacstrap "${ARCH_MNT_PATH}" base-devel
    if is_profile_enabled "python"; then
        echo "Installing python development packages"
        pacstrap "${ARCH_MNT_PATH}" python python-pip python-setuptools
    fi
}
# shellcheck disable=SC2048
# shellcheck disable=SC2086
install_kernel() {
    echo "Installing kernel"
    pacstrap "${ARCH_MNT_PATH}" ${ARCH_KERNELS[*]}
    is_profile_enabled "firmware" && pacstrap "${ARCH_MNT_PATH}" linux-firmware
    is_profile_enabled "parabola_firmware" && pacstrap "${ARCH_MNT_PATH}" linux-libre-firmware
    if is_profile_enabled "kernel_headers"; then
        for kernel in "${ARCH_KERNELS[@]}"; do
            case "${kernel}" in
                "linux") pacstrap "${ARCH_MNT_PATH}" linux-headers ;;
                "linux-lts") pacstrap "${ARCH_MNT_PATH}" linux-lts-headers ;;
                "linux-hardened") pacstrap "${ARCH_MNT_PATH}" linux-hardened-headers ;;
                "linux-zen") pacstrap "${ARCH_MNT_PATH}" linux-zen-headers ;;
                "linux-libre") pacstrap "${ARCH_MNT_PATH}" linux-libre-headers ;;
                "linux-libre-hardened") pacstrap "${ARCH_MNT_PATH}" linux-libre-hardened-headers ;;
                *)
                    if [[ -z "${ARCH_KERNELS_HEADERS[*]}" ]]; then
                        pacstrap "${ARCH_MNT_PATH}" "${kernel}-headers"
                    else
                        for kernel_headers in "${ARCH_KERNELS_HEADERS[@]}"; do
                            pacstrap "${ARCH_MNT_PATH}" "${kernel_headers}"
                        done
                    fi
                    ;;
            esac
        done
    fi
}
install_zfs() {
    cat <<EOF >> "${ARCH_MNT_PATH}"/etc/pacman.conf
[archzfs]
# Origin Server - France
Server = http://archzfs.com/\$repo/x86_64
# Mirror - Germany
Server = http://mirror.sum7.eu/archlinux/archzfs/\$repo/x86_64
# Mirror - Germany
Server = https://mirror.biocrafting.net/archlinux/archzfs/\$repo/x86_64
# Mirror - India
Server = https://mirror.in.themindsmaze.com/archzfs/\$repo/x86_64
# Mirror - US
Server = https://zxcvfdsa.com/archzfs/\$repo/\$arch
EOF
    arch-chroot "${ARCH_MNT_PATH}" <<EOF
pacman-key --recv-keys ${ARCHZFS_REPO_KEY}
pacman-key --lsign-key ${ARCHZFS_REPO_KEY}
pacman -Syu --noconfirm
EOF
    if is_profile_enabled "zfs_force_dkms"; then
        arch-chroot "${ARCH_MNT_PATH}" <<EOF
pacman -S --noconfirm zfs-dkms
depmod
modprobe zfs
EOF
    else
        for kernel in "${ARCH_KERNELS[@]}"; do
            case "${kernel}" in
                "linux") pacstrap "${ARCH_MNT_PATH}" zfs-linux ;;
                "linux-lts") pacstrap "${ARCH_MNT_PATH}" zfs-linux-lts ;;
                "linux-hardened") pacstrap "${ARCH_MNT_PATH}" zfs-linux-hardened ;;
                "linux-zen") pacstrap "${ARCH_MNT_PATH}" zfs-linux-zen ;;
                *)
                    if is_profile_enabled "!dkms"; then
                        pacstrap "${ARCH_MNT_PATH}" "${ARCH_ZFS_MODULE_PKGNAME}"
                    else
                        arch-chroot "${ARCH_MNT_PATH}" <<EOF
pacman -S --noconfirm zfs-dkms
depmod
modprobe zfs
EOF
                    fi
                    ;;
            esac
        done
    fi
}
setup_mdadm() {
    mdadm --detail --scan >> "${ARCH_MNT_PATH}"/etc/mdadm.conf
    pacstrap "${ARCH_MNT_PATH}" mdadm
}
# shellcheck disable=SC2048
# shellcheck disable=SC2086
install_extra_software() {
    if [[ -n "${ARCH_EXTRA_SOFTWARE[*]}" ]]; then
        echo "Installing extra software"
        pacstrap "${ARCH_MNT_PATH}" ${ARCH_EXTRA_SOFTWARE[*]}
    fi
}
change_root_password() {
    echo "Setting root password"
    echo "root:${ARCH_ROOT_PASSWD}" | arch-chroot "${ARCH_MNT_PATH}" chpasswd --encrypted
}
create_regular_user() {
    arch-chroot "${ARCH_MNT_PATH}" useradd -m -g users -G wheel,games,power,optical,storage,scanner,lp,audio,video -s /bin/bash "${ARCH_USER}"
    echo "Setting ${ARCH_USER} password"
    echo "${ARCH_USER}:${ARCH_USER_PASSWD}" | arch-chroot "${ARCH_MNT_PATH}" chpasswd --encrypted
    sed -i "s/#\s*%wheel ALL=\(ALL:ALL\) ALL/%wheel ALL=\(ALL:ALL\) ALL/g" "${ARCH_MNT_PATH}"/etc/sudoers
    if is_profile_enabled "zfs"; then
        arch-chroot "${ARCH_MNT_PATH}" <<EOF
zfs allow ${ARCH_USER} create,mount,mountpoint,snapshot ${ARCH_ZFS_HOME_DATASET}
chown -R ${ARCH_USER} /home/${ARCH_USER}
EOF
    fi
}
setup_crypttab() {
    if is_profile_enabled "swap_encrypt"; then
        cat <<EOF >> /etc/crypttab
swap           ${ARCH_SWAP_CRYPTDEV}                                 /dev/urandom            swap,cipher=aes-xts-plain64:sha256,size=512
EOF
        cat <<EOF >> /etc/fstab
/dev/mapper/swap        none            swap            defaults        0 0
EOF
    fi
}
copy_custom_files() {
    echo "Copying custom files to the target system"
    cp -r "${CONFIG_DIR}"/custom_files/* "${ARCH_MNT_PATH}"
}
generate_initramfs() {
    local initcpio_conf
    initcpio_conf="${ARCH_MNT_PATH}"/etc/mkinitcpio.conf
    echo "Setting up mkinitcpio options"
    if [[ -n "${ARCH_INITRD_MODULES[*]}" ]]; then
        sed -ri "s/#?MODULES=\(.*\)/MODULES=\(${ARCH_INITRD_MODULES[*]}\)/g" "${initcpio_conf}"
    fi
    if [[ -n "${ARCH_INITRD_BINARIES[*]}" ]]; then
        sed -ri "s/#?BINARIES=\(.*\)/BINARIES=\(${ARCH_INITRD_BINARIES[*]}\)/g" "${initcpio_conf}"
    fi
    if [[ -n "${ARCH_INITRD_FILES[*]}" ]]; then
        sed -ri "s/#?FILES=\(.*\)/FILES=\(${ARCH_INITRD_FILES[*]}\)/g" "${initcpio_conf}"
    fi
    if [[ -n "${ARCH_INITRD_HOOKS[*]}" ]]; then
        if [[ -d "${CONFIG_DIR}/initcpio_hooks" ]] && [[ -d "${CONFIG_DIR}/initcpio_install" ]]; then
            echo "Copying mkinitcpio custom hooks files"
            cp "${CONFIG_DIR}"/initcpio_hooks/* "${ARCH_MNT_PATH}"/etc/initcpio/hooks
            cp "${CONFIG_DIR}"/initcpio_install/* "${ARCH_MNT_PATH}"/etc/initcpio/install
        fi
        sed -ri "s/#?HOOKS=\(.*\)/HOOKS=\(${ARCH_INITRD_HOOKS[*]}\)/g" "${initcpio_conf}"
    fi
    if [[ -n "${ARCH_INITRD_COMPRESSION[*]}" ]]; then
        sed -ri "s/#?COMPRESSION=\"${ARCH_INITRD_COMPRESSION}\"/COMPRESSION=\"${ARCH_INITRD_COMPRESSION}\"/g" "${initcpio_conf}"
    fi
    if [[ -n "${ARCH_INITRD_COMPRESSION_OPTS[*]}" ]]; then
        sed -ri "s/#?COMPRESSION_OPTIONS=\(.*\)/COMPRESSION_OPTIONS=\(${ARCH_INITRD_COMPRESSION_OPTS[*]}\)/g" "${initcpio_conf}"
    fi
    if [[ -n "${ARCH_INITRD_MODULES_DECOMPRESS}" ]]; then
        sed -ri "s/#?MODULES_DECOMPRESS=\".*\"/MODULES_DECOMPRESS=\"yes\"/g" "${initcpio_conf}"
    fi
    is_profile_enabled "zfs" && arch-chroot "${ARCH_MNT_PATH}" zgenhostid
    echo "Generating initramfs"
    arch-chroot "${ARCH_MNT_PATH}" mkinitcpio -P
}
install_grub() {
    if is_profile_enabled "zfs"; then
        echo "ZPOOL_VDEV_NAME_PATH=YES" >> "${ARCH_MNT_PATH}"/etc/environment
    fi
    pacstrap "${ARCH_MNT_PATH}" grub
    case "${ARCH_BOOT_MODE}" in
        "uefi")
            pacstrap "${ARCH_MNT_PATH}" dosfstools efibootmgr mtools
            mkdir -p "${ARCH_MNT_PATH%%/}${ARCH_GRUB_BOOT_DIR}"
            mkdir -p "${ARCH_MNT_PATH%%/}${ARCH_GRUB_EFI_DIR}"
            arch-chroot "${ARCH_MNT_PATH}" bash -c "
                source /etc/environment;
               "ZPOOL_VDEV_NAME_PATH=YES grub-install --target x86_64-efi \
                --boot-directory ${ARCH_GRUB_BOOT_DIR} \
--efi-directory ${ARCH_GRUB_EFI_DIR} \
              --efi-directory ${ARCH_GRUB_EFI_DIR} --bootloader-id ${ARCH_EFI_BOOTLOADER_ID} --removable"
            ;;
        "legacy_bios")
            for boot_dev in "${ARCH_BOOT_DEVICES[@]}"; do
                arch-chroot "${ARCH_MNT_PATH}" bash -c "
                    source /etc/environment;
                   "ZPOOL_VDEV_NAME_PATH=YES grub-install ${boot_dev}"
            done
            ;;
        *)
            die "install_grub: unknown boot mode '${ARCH_BOOT_MODE}'"
            ;;
    esac
    [[ -f "${CONFIG_DIR}/grub_default.conf" ]] && cp "${CONFIG_DIR}"/grub_default.conf "${ARCH_MNT_PATH}"/etc/default/grub
    sleep 1
    arch-chroot "${ARCH_MNT_PATH}" bash -c "source /etc/environment;"ZPOOL_VDEV_NAME_PATH=YES grub-mkconfig -o /boot/grub/grub.cfg"
}
enable_extra_services() {
    echo "Enabling extra services"
    for systemd_service in "${ARCH_POSTINST_SERVICES[@]}"; do
        arch-chroot "${ARCH_MNT_PATH}" systemctl enable "${systemd_service}"
    done
}
run_extra_commands() {
    echo "Running post-install extra commands"
    arch-chroot "${ARCH_MNT_PATH}" "${ARCH_POSTINST_CMDS}"
}
snapshot_installation() {
    echo "Taking snapshot of the ZFS datasets"
    zfs snapshot -r "${ARCH_ZFS_BOOT_POOL}@${ARCH_ZFS_SNAPSHOT_NAME}"
    zfs snapshot -r "${ARCH_ZFS_ROOT_POOL}@${ARCH_ZFS_SNAPSHOT_NAME}"
}
validate_args
check_profiles_deps
if [[ -n "${APPLY}" ]]; then
    install_base_system
    sleep 1; is_profile_enabled "genfstab" && generate_fstab
    set_timezone
    set_locale
    set_keymap
    set_hostname
    sleep 2; enable_dhcpcd
    sleep 2; is_profile_enabled "devel" && install_devel_pkgs
    sleep 2; install_kernel
    sleep 2; is_profile_enabled "zfs" && install_zfs
    sleep 2; is_profile_enabled "raid" && setup_mdadm
    [[ -n "${ARCH_EXTRA_SOFTWARE[*]}" ]] && install_extra_software
    sleep 2; change_root_password
    sleep 2; create_regular_user
    sleep 2; setup_crypttab
    [[ -d "${CONFIG_DIR}/custom_files" ]] && copy_custom_files
    sleep 2; generate_initramfs
    sleep 2; install_grub
    sleep 2; [[ -n "${ARCH_POSTINST_SERVICES[*]}" ]] && enable_extra_services
    sleep 2; [[ -n "${ARCH_POSTINST_CMDS}" ]] && run_extra_commands
    if is_profile_enabled "zfs" && is_profile_enabled "zfs_snapshot_install"; then
        sleep 2; snapshot_installation
    fi
    echo "Done!"
else
    echo "Execute script with --apply option to confirm destructive action"
fi
 
                