arch-zbm/setup.sh

780 lines
27 KiB
Bash
Raw Normal View History

#!/bin/bash
2023-02-19 21:55:47 +01:00
declare this_script_url
this_script_url="${SCRIPT_URL:?}"
declare zpool_name zfs_arch_dataset_name
zpool_name='zpool'
zfs_arch_dataset_name='archlinux'
# https://unix.stackexchange.com/a/48550
set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR
declare zpool_drive efi_drive
function we_are_changerooted () {
if [ "$(stat -c '%d:%i' '/')" != "$(stat -c '%d:%i' '/proc/1/root/.')" ]; then
return 0
else
return 1
fi
}
function set_ntp () {
#1.1
timedatectl set-ntp true
}
function resize_cow_space () {
#1.2
mount -o remount,size='50%' /run/archiso/cowspace
}
function update_pacman_db () {
#1.3
printf -- '%s\n' 'Refreshing mirror list ...'
systemctl start reflector
# In an ISO and for the minimal number of packages we need we do not
# care about partial upgrades
pacman -Sy
}
function install_pkgs () {
#1.4
printf -- '%s\n' 'Installing packages ...'
pacman -S --needed --noconfirm "${@}"
}
function prepare_zfsbootmenu_efi_bin_pkg () {
#1.5
# Our Arch Linux will use prebuilt ZFSBootMenu UEFI image files for ease
# of use. We'd like to install them from within our chroot but the AUR
# package 'zfsbootmenu-efi-bin' that we're using currently (Friday,
# October 20, 2023) identifies its target EFI system partition (ESP) by
# doing 'lsblk' and checking the partition type name. Since within
# arch-chroot there's no '/run/udev' but only an empty '/run' tmpfs
# 'lsblk' cannot see partition type names and some other pieces of info.
#
# See also
# https://gitlab.archlinux.org/archlinux/arch-install-scripts/-/issues/24
#
# Thus within arch-chroot the installation of 'zfsbootmenu-efi-bin' and
# even 'makepkg -s' fails. We build the package in our Arch Linux live
# CD ISO image and copy the resulting file into our chroot.
#
# We circle back to the package file when we chroot into Arch and
# install_zbm_image().
pacman_cache_dir='/var/cache/pacman/pkg'
chroot_pacman_cache_dir='/mnt'"${pacman_cache_dir}"
git_zbm_efi_bin_dir='/tmp/zfsbootmenu-efi-bin'
sudo -u 'arch' git -C '/tmp' clone 'https://aur.archlinux.org/zfsbootmenu-efi-bin.git'
# We briefly bind-mount our ESP into '/efi' before we build the package.
# Its PKGBUILD will then find the ESP at the correct location and write
# a package file with that internal location ('/efi').
mkdir -p '/efi'
mount --bind '/mnt/efi' '/efi'
pushd "${git_zbm_efi_bin_dir}"
sudo -u 'arch' makepkg -s || {
printf -- '%s\n' 'Failed building zfsbootmenu-efi-bin package. Exiting ...'
exit 77
}
popd
umount '/efi'
rmdir '/efi'
package_file_abs="$(find "${git_zbm_efi_bin_dir}" -type f -iname '*pkg.tar.zst')"
package_file="$(basename "${package_file_abs}")"
mkdir -p "${chroot_pacman_cache_dir}"
rsync -av "${package_file_abs}" "${chroot_pacman_cache_dir}"'/'"${package_file}" || {
printf -- '%s\n' 'Failed rsyncing zfsbootmenu-efi-bin package file into chroot. Exiting ...'
exit 77
}
export ZFSBOOTMENU_EFI_BIN_PKG_PATH="${pacman_cache_dir}"'/'"${package_file}"
rm -rf "${git_zbm_efi_bin_dir}"
}
function install_zfs () {
#1.6
declare reset_colors='\033[0m'
curl -s 'https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init' | bash
2023-02-19 21:01:36 +01:00
printf -- "${reset_colors}"
}
function get_partitions () {
declare partitions_json
partitions_json="$(lsblk --output PATH,PARTTYPE --json --tree)" || return 1
printf -- '%s' "${partitions_json}"
return 0
}
function get_part_parent () {
local child_partition parent_partition
child_partition="${1:?}"
parent_partition="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.children[].path=="'"${child_partition:?}"'") | .path')"
printf -- '%s' "${parent_partition}"
return 0
}
function get_parts () {
local zfs_install_drive
declare parttype parts
parttype="${1:?}"
zfs_install_drive="${2:-}"
case "${parttype}" in
zfs)
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | .children[] | select(.parttype=="6a85cf4d-1dd2-11b2-99a6-080020736631") | .path')" || exit 1
;;
efi)
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.path=="'"${zfs_install_drive:?}"'") | .children[] | select(.parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b") | .path')" || exit 1
;;
*)
>2 printf -- '%s\n' 'Unknown partition type '"'"'"${parttype}"'"'"', exiting ...'
exit 77
;;
esac
printf -- '%s' "${parts}"
return 0
}
function we_have_exactly_one_part () {
local parttype parts_list parts_count
parttype="${1:?}"
parts_list="${2:?}"
parts_count="$(wc -l <<<"${parts_list}")"
if [[ "$(wc -c <<<"${parts_list}")" -gt '1' ]]; then
case "${parts_count}" in
0)
>2 printf -- '%s\n' 'No '"${parttype^^}"' partition found. Exiting ...'
exit 77
;;
1)
return 0
;;
*)
return 1
;;
esac
>2 printf -- '%s\n' 'Partition list does not look valid. Cowardly exiting ...'
exit 77
fi
}
function get_drive_id () {
local drive_id_list drive_id_single
drive_id_list="$(find -L /dev/disk/by-partuuid -samefile "${1:?}" | sort)"
drive_id_single="$(head -n1 <<<"${drive_id_list}")"
if [[ "$(wc -l <<<"${drive_id_single}")" -eq '1' ]] && [[ "$(wc -c <<<"${drive_id_single}")" -gt '1' ]]; then
printf -- '%s' "${drive_id_single}"
return 0
fi
>2 printf -- '%s\n' 'No '"'${1:?}'"' partition entry in /dev/disk/by-partuuid, exiting ...'
exit 77
}
function select_part () {
2023-02-23 01:56:09 +01:00
local parts enriched_parts enriched_parts_count part_number part_type zfs_install_drive
declare part
part_type="${1:?}" # 'efi' or 'zfs'
zfs_install_drive="${2:-}"
if [[ "${zfs_install_drive}" ]]; then
# This is intended to find correct EFI partition
parts="$(get_parts "${part_type}" "${zfs_install_drive}")"
else
parts="$(get_parts "${part_type}")"
fi
if we_have_exactly_one_part "${part_type}" "${parts}"; then
part="${parts}"
else
2> printf -- '%s\n' 'More than one '"${part_type^^}"' partition to pick for installation. Cowardly exiting ...'
exit 77
fi
printf -- '%s' "${part}"
return 0
}
function no_zpool_exists () {
declare zpool_list
zpool_list="$(zpool list -H)"
[[ "$(wc -l <<<"${zpool_list}")" -le '1' ]] && [[ "$(wc -c <<<"${zpool_list}")" -le '1' ]] && return 0
return 1
}
function set_zpool_password () {
# May or may not have a newline at the end, ZFS doesn't care
printf -- '%s' 'password' > '/etc/zfs/'"${zpool_name}"'.key'
chmod '000' '/etc/zfs/'"${zpool_name}"'.key'
}
function import_pool () {
zpool import -d '/dev/disk/by-partuuid' -R '/mnt' "${zpool_name}" -N -f
[[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && zfs load-key "${zpool_name}"
}
function create_pool () {
# Create a temporary pool that is not cached
#
# Add zfsprops 'compression' unless environment variable
# ARCHZBM_ZFSPROPS_NO_COMPRESSION is set to any value.
#
# Add zfsprops 'encryption' along with 'keyformat' and a 'keylocation'
# unless environment variable ARCHZBM_ZFSPROPS_NO_ENCRYPTION is set to
# any value.
zpool create -f \
-o 'ashift=12' \
-o 'autotrim=on' \
-O 'acltype=posix' \
$([[ ! "${ARCHZBM_ZFSPROPS_NO_COMPRESSION}" ]] && \
printf -- '%s ' \
'-O compression=on') \
-O 'relatime=on' \
-O 'xattr=sa' \
$([[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && \
printf -- '%s ' \
'-O encryption=on' \
'-O keyformat=passphrase' \
'-O keylocation=file:///etc/zfs/'"${zpool_name}"'.key') \
-O 'normalization=formD' \
-O 'mountpoint=none' \
-O 'canmount=off' \
-O 'devices=off' \
-R '/mnt' \
"${zpool_name}" "${1:?}"
}
function create_root_dataset () {
zfs create -o mountpoint=none "${zpool_name}"'/root'
# zfs set org.zfsbootmenu:commandline="ro quiet" "${zpool_name}"'/root'
zfs set org.zfsbootmenu:commandline="ro" "${zpool_name}"'/root'
}
function create_system_dataset () {
zfs create -o 'mountpoint=/' -o 'canmount=noauto' "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
zgenhostid
zpool set bootfs="${zpool_name}"'/root/'"${zfs_arch_dataset_name}" "${zpool_name}"
zfs mount "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
}
function create_home_dataset () {
zfs create -o 'mountpoint=/' -o 'canmount=off' "${zpool_name}"'/data'
zfs create "${zpool_name}"'/data/home'
}
function export_pool () {
zpool export "${zpool_name}"
}
function setup_zpool () {
#1.7
local drive_by_id
zpool_drive="$(select_part 'zfs')"
drive_by_id="$(get_drive_id "${zpool_drive}")"
[[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && set_zpool_password
if no_zpool_exists; then
create_pool "${drive_by_id}"
create_root_dataset
create_system_dataset
create_home_dataset
export_pool
import_pool
else
printf -- '%s\n' 'A zpool already exists, that is unexpected. Cowardly exiting 1 ...'
exit 1
fi
}
function mount_system () {
#1.8
zfs mount "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
zfs mount -a
local zfs_parent efi_part
zfs_parent="$(get_part_parent "${zpool_drive:?}")"
efi_drive="${zfs_parent}"
efi_part="$(select_part 'efi' "${zfs_parent:?}")"
mkdir -p '/mnt/efi'
mount "${efi_part}" '/mnt/efi'
}
function copy_zpool_cache () {
#1.9
mkdir -p '/mnt/etc/zfs'
zpool set 'cachefile=/etc/zfs/'"${zpool_name}"'.cache' "${zpool_name}"
}
function pacman_dl_parallel () {
#2.4
# We're setting this in Arch Linux ISO CD while we install proper Arch.
# No need to revert this later as it is ephemeral anyway.
sed -ri -e 's'$'\x1''^.*?(ParallelDownloads)[^\r\n\f]*'$'\x1''\1 = 20'$'\x1''g' '/etc/pacman.conf'
}
function pacman_dont_check_space () {
# See pacman bug comment
# https://bugs.archlinux.org/task/45070#comment142712
#
# When we pacstrap onto ZFS pacman incorrectly calculates and
# overestimates required disk space. We instead assume an installation
# gets done with at least a 10 GiB drive which is plenty. Skip pacman's
# size check.
#
# We're setting this in Arch Linux ISO CD while we install proper Arch.
# No need to revert this later as it is ephemeral anyway.
sed -ri -e 's'$'\x1''^.*?(CheckSpace)([^\r\n\f]*)'$'\x1''#\1\2'$'\x1''g' '/etc/pacman.conf'
}
function install_archlinux () {
#1.10
pacman_dl_parallel
pacman_dont_check_space
pacstrap /mnt \
base \
base-devel \
linux \
linux-headers \
linux-firmware \
amd-ucode \
efibootmgr \
vim \
git \
iwd \
networkmanager \
network-manager-applet \
dialog \
os-prober \
reflector \
bluez \
bluez-utils \
man-db \
xdg-utils \
xdg-user-dirs
}
function gen_fstab () {
#1.11
genfstab -U /mnt | grep -v "${zpool_name}" | tr -s '\n' | sed -r -e 's/\/mnt//' -e '/./,$!d' > '/mnt/etc/fstab'
}
function configure_hosts_file () {
cat > '/mnt/etc/hosts' <<EOF
#<ip-address> <hostname.domain.org> <hostname>
127.0.0.1 localhost ${1}
::1 localhost ${1}
EOF
}
function set_hostname () {
#1.12
declare new_hostname
install_pkgs 'pwgen'
new_hostname="$(pwgen --no-numerals --no-capitalize --ambiguous 8)"
printf -- '%s\n' "${new_hostname}" > '/mnt/etc/hostname'
configure_hosts_file "${new_hostname}"
}
function set_locale () {
#1.13
printf -- '%s\n' \
'KEYMAP=de-latin1' \
'FONT=Lat2-Terminus16' \
'FONT_MAP=8859-1' \
> '/mnt/etc/vconsole.conf'
sed -ri -e 's'$'\x1''^(#)(en_US.UTF-8[^\r\n\f]*)'$'\x1''\2'$'\x1''g' '/mnt/etc/locale.gen'
printf -- '%s\n' 'LANG=en_US.UTF-8' > '/mnt/etc/locale.conf'
}
function add_zfs_hook_to_initramfs () {
#1.14
# Add zfs hook, remove fsck hook from initramfs.
sed -ri \
-e 's'$'\x1''(HOOKS=)(.*?[\(| ])(filesystems)([\)| ][^\r\n\f]*)'$'\x1''\1\2zfs \3\4'$'\x1''g' \
-e 's'$'\x1''((\()(fsck)(\)))'$'\x1''\2\4'$'\x1''g' \
-e 's'$'\x1''(([[:space:]]+)(fsck)|(fsck)([[:space:]]+))'$'\x1'''$'\x1''g' \
'/mnt/etc/mkinitcpio.conf'
# Also unless encryption's unwanted add plain text key file into
# initramfs since it's living inside an encrypted pool anyway.
[[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && sed -ri \
-e 's'$'\x1''^(FILES=)[^\r\n\f]*'$'\x1''\1(/etc/zfs/'"${zpool_name}"'.key)'$'\x1''g' \
'/mnt/etc/mkinitcpio.conf'
}
function set_initramfs_build_list () {
#1.15
# No need to build fallback initramfs, our new fallback is ZFS snapshots
sed -ri \
-e '/^#/d' \
-e '/^$/d' \
-e '/^fallback/d' \
-e 's'$'\x1''^(PRESETS=)[^\r\n\f]*'$'\x1''\1('"'"'default'"'"')'$'\x1''g' \
'/mnt/etc/mkinitcpio.d/linux.preset'
# Remove any existing fallback initramfs files
find '/mnt/boot' -type f -regextype posix-extended -iregex '^/mnt/boot/initramfs-.*?-fallback.img' -delete
}
function add_zfs_files_to_new_os () {
#1.16
for zfs_file in '/etc/hostid' '/etc/zfs/zpool.cache' $([[ ! "${ARCHZBM_ZFSPROPS_NO_ENCRYPTION}" ]] && printf -- '%s' '/etc/zfs/'"${zpool_name}"'.key'); do
rsync -av --itemize-changes {'','/mnt'}"${zfs_file}"
done
}
function enter_chroot () {
#2.1
arch-chroot /mnt /bin/bash -xe <<EOF
curl --silent '${this_script_url}' | bash
EOF
}
function get_pkg_info () {
declare from_where local_pkg_info local_pkg_version version_search
from_where="${1}"
version_search='/Version/{print $3}'
pkg_info="$(paru -$([[ "${from_where}" == 'local' ]] && printf -- '%s' 'Q' || printf -- '%s' 'S')i "${2}" 2>&1)"
if [[ "${from_where}" == 'local' ]] && grep -Piq -- '^error: package .*? was not found' <<<"${pkg_info}"; then
return 1
else
local_pkg_version="$(awk "${version_search}" <<<"${pkg_info}")"
fi
printf -- '%s' "${local_pkg_version}"
return 0
}
function paru_with_zfs_first () {
if [[ "${#}" -eq '0' ]]; then
declare -A local_pkg_info
/usr/bin/paru -Sy
if local_pkg_info['zfs-dkms']="$(get_pkg_info 'local' 'zfs-dkms')" && local_pkg_info['zfs-utils']="$(get_pkg_info 'local' 'zfs-utils')"; then
local_pkg_info['zfs-dkms']="$(get_pkg_info 'local' 'zfs-dkms')"
local_pkg_info['zfs-utils']="$(get_pkg_info 'local' 'zfs-utils')"
declare -A remote_pkg_info
remote_pkg_info['zfs-dkms']="$(get_pkg_info 'remote' 'zfs-dkms')"
remote_pkg_info['zfs-utils']="$(get_pkg_info 'remote' 'zfs-utils')"
/usr/bin/paru -S --needed archlinux-keyring
if [[ "${local_pkg_info['zfs-dkms']}" == "${remote_pkg_info['zfs-dkms']}" ]] && \
[[ "${local_pkg_info['zfs-utils']}" == "${remote_pkg_info['zfs-utils']}" ]]; then
/usr/bin/paru -Su
else
/usr/bin/paru -Sy 'zfs-dkms' 'zfs-utils' \
--assume-installed zfs-dkms="${local_pkg_info['zfs-dkms']}" \
--assume-installed zfs-dkms="${remote_pkg_info['zfs-dkms']}" \
--assume-installed zfs-utils="${local_pkg_info['zfs-utils']}" \
--assume-installed zfs-utils="${remote_pkg_info['zfs-utils']}"
/usr/bin/paru -Su
fi
else
/usr/bin/paru -S --needed archlinux-keyring
/usr/bin/paru -Su
fi
else
/usr/bin/paru "${@}"
fi
}
function create_unpriv_user () {
account_name="${1:?}"
full_name="${2:-${account_name}}"
authorized_keys_abs_path='/home/'"${account_name}"'/.ssh/authorized_keys'
useradd --create-home --shell '/bin/bash' --comment 'Personal account of '"${full_name}" --user-group "${account_name}"
chfn --full-name "${full_name}" "${account_name}"
mkdir -p "$(dirname "${authorized_keys_abs_path}")"
touch "${authorized_keys_abs_path}"
chown -R "${account_name}"':' '/home/'"${account_name}"; chmod -R 'u=rwX,go=' "$(dirname "${authorized_keys_abs_path}")"
}
function unleash_makepkg () {
#2.5
local path_prefix
path_prefix='/mnt'
if we_are_changerooted; then
path_prefix=''
fi
sed -ri \
-e 's'$'\x1''^(#?(MAKEFLAGS=))[^\r\n\f]*'$'\x1''\2"-j$(nproc --ignore 1)"'$'\x1''g' \
"${path_prefix}"'/etc/makepkg.conf'
}
function get_aur_helper () {
#2.7
create_unpriv_user 'build'
usermod --append --groups 'wheel' 'build'
printf -- '%s\n' '%wheel ALL=(ALL:ALL) NOPASSWD: ALL' > '/etc/sudoers.d/10-wheel-group-no-passwd-prompt'
pushd /tmp
git clone 'https://aur.archlinux.org/paru.git'
chown -R 'build:' 'paru'
pushd 'paru'
sudo --user 'build' makepkg -si --noconfirm
popd
rm -rf 'paru'
popd
alias paru='paru_with_zfs_first'
}
function paru_install () {
declare -a paru_install_packages
[[ "${1}" ]] && while :; do
case "${1}" in
-[[:alnum:]]*)
printf -- '%s\n' \
'Short-form argument '"'${1}'"' not supported for function '"'${FUNCNAME[0]}()'"'. Only known accepted argument' \
'is '"'"'--replace-conflicting'"'"' without a value given. Exiting ...'
exit 77
;;
--replace-conflicting)
pacman_force_yes='true'
shift
continue
;;
--*)
printf -- '%s\n' \
'Long-form argument '"'${1}'"' not supported for function '"'${FUNCNAME[0]}()'"'. Only known accepted argument' \
'is '"'"'--replace-conflicting'"'"' without a value given. Exiting ...'
exit 77
;;
'')
# All arguments processed
break
;;
*)
paru_install_packages+=("${1}")
shift
;;
esac
done || {
printf -- '%s\n' \
'No argument '"'${1}'"' given for function '"'${FUNCNAME[0]}'"'. Exiting ...'
exit 77
}
if [[ "${pacman_force_yes}" ]]; then
yes 'y' | sudo --user build paru -S "${paru_install_packages[@]}"
unset -v pacman_force_yes
else
sudo --user build paru -S --noconfirm "${paru_install_packages[@]}"
fi
}
function install_zbm_image () {
#2.8
# This takes image files written earlier in our Arch Linux live CD ISO
# image at prepare_zfsbootmenu_efi_bin_pkg() and installs them via their
# locally built package file. When done we delete the manually built
# package file from pacman's cache dir.
pacman -U "${ZFSBOOTMENU_EFI_BIN_PKG_PATH}" --noconfirm
rm "${ZFSBOOTMENU_EFI_BIN_PKG_PATH}"
}
function keep_initiramfs_root_only_rw () {
#2.3
declare systemd_local_admin_override_path unit_name
systemd_local_admin_override_path='/etc/systemd/system'
unit_name='chmod-initramfs'
path_unit="${systemd_local_admin_override_path%/}"'/'"${unit_name}"'.path'
service_unit="${systemd_local_admin_override_path%/}"'/'"${unit_name}"'.service'
cat > "${path_unit}" <<"EOF"
[Unit]
Description=chmod initramfs to be root-read-writable only
[Path]
PathChanged=/boot/initramfs-linux.img
[Install]
WantedBy=multi-user.target
WantedBy=system-update.target
EOF
cat > "${service_unit}" <<"EOF"
[Unit]
Description=chmod initramfs to be root-read-writable only
[Service]
Type=oneshot
ExecStart=/usr/bin/chmod 600 /boot/initramfs-linux.img
EOF
systemctl enable "${path_unit}"
}
function add_motd_getting_started_msg () {
#2.6
cat > '/etc/motd' <<"EOF"
####################
GUI basics:
paru -S xorg plasma-meta kde-applications-meta sddm
localectl set-x11-keymap de
####################
EOF
}
function install_os_in_chroot () {
#2.2
### Reinit keyring
# As keyring is initialized at boot, and copied to the install dir with pacstrap, and ntp is running
# Time changed after keyring initialization, it leads to malfunction
# Keyring needs to be reinitialised properly to be able to sign keys.
rm -rf '/etc/pacman.d/gnupg'
pacman-key --init
pacman-key --populate archlinux
pacman -S archlinux-keyring --noconfirm
keep_initiramfs_root_only_rw #2.3
pacman_dl_parallel #2.4
unleash_makepkg #2.5
add_motd_getting_started_msg #2.6
get_aur_helper #2.7
paru_install --replace-conflicting 'paru-bin'
paru_install 'zfs-dkms' 'zfs-utils' 'rsync'
hwclock --systohc
locale-gen
source /etc/locale.conf
mkinitcpio -P
# Install ZFSBootMenu image
install_zbm_image #2.8
}
function set_root_pw () {
#3.2
printf -- '%s\n' 'root:password' | chpasswd --crypt-method 'SHA512' --root '/mnt'
}
function configure_networking () {
#3.3
cat > '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
[Match]
Name=en*
[Network]
DHCP=ipv4
IPForward=yes
[DHCP]
UseDNS=yes
RouteMetric=10
EOF
systemctl enable 'systemd-networkd' --root='/mnt'
systemctl disable 'systemd-networkd-wait-online' --root='/mnt'
}
function configure_dns () {
#3.4
rm '/mnt/etc/resolv.conf'
ln -s '/run/systemd/resolve/stub-resolv.conf' '/mnt/etc/resolv.conf'
# Optionally you may want /etc/systemd/network/50-wired.network to use
# UseDNS=no and hardcode DNS server(s) here:
# sed -i 's/^#DNS=.*/DNS=1.1.1.1/' /mnt/etc/systemd/resolved.conf
systemctl enable 'systemd-resolved' --root='/mnt'
}
function configure_reflector () {
#3.5
systemctl enable 'reflector.service' 'reflector.timer' --root='/mnt'
}
function configure_zfs () {
#3.6
systemctl enable 'zfs-import-cache' 'zfs-mount' 'zfs-import.target' 'zfs.target' --root='/mnt'
}
function configure_zfs_mount_gen () {
#3.7
mkdir -p '/mnt/etc/zfs/zfs-list.cache'
touch '/mnt/etc/zfs/zfs-list.cache/'"${zpool_name}"
zfs list -H -o name,mountpoint,canmount,atime,relatime,devices,exec,readonly,setuid,nbmand | sed 's/\/mnt//' > '/mnt/etc/zfs/zfs-list.cache/'"${zpool_name}"
systemctl enable 'zfs-zed.service' --root='/mnt'
}
function configure_zfsbootmenu () {
#3.8
zfs set org.zfsbootmenu:commandline='rw nowatchdog rd.vconsole.keymap=de-latin1' "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
}
function get_disks_with_one_efipart () {
local disks_with_one_efipart
# Find disks that have exactly one EFI partition and where that EFI
# partition is partition number 1. We expect exactly one disk to meet
# these criteria. Anything else and we bail.
disks_with_one_efipart="$(lsblk --output PATH,PARTTYPE --json --tree | jq --raw-output '.[][] | select(.children | length > 0) | select( any (.children[]; (.path | test("^[^[:digit:]]+1")) and (.parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) and ([select(.children[].parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b")] | length == 1) ) | .path')"
if [[ "$(wc -l <<<"${disks_with_one_efipart}")" -eq '1' ]] && [[ "$(wc -c <<<"${disks_with_one_efipart}")" -gt '1' ]]; then
printf -- '%s' "${disks_with_one_efipart}"
return 0
fi
return 1
}
function add_zbm_to_efi () {
#3.9
if ! efibootmgr | grep -Piq -- 'ZFSBootMenu'; then
local efi_disks_list
efi_disks_list="$(get_disks_with_one_efipart)"
if grep -Piq -- '^'"${efi_drive}"'$' <<<"${efi_disks_list}"; then
efibootmgr --disk "${efi_drive}" \
--part 1 \
--create \
--label "ZFSBootMenu" \
--loader "\EFI\ZBM\vmlinuz.efi" \
--verbose
fi
fi
}
function umount_all () {
#3.10
umount '/mnt/efi'
zfs umount -a
zpool export "${zpool_name}"
}
function finalize_os_setup () {
#3.1
set_root_pw #3.2
configure_networking #3.3
configure_dns #3.4
configure_reflector #3.5
configure_zfs #3.6
configure_zfs_mount_gen #3.7
configure_zfsbootmenu #3.8
add_zbm_to_efi #3.9
umount_all #3.11
}
function main () {
if we_are_changerooted; then
install_os_in_chroot #2.2
else
set_ntp #1.1
resize_cow_space #1.2
update_pacman_db #1.3
install_pkgs 'base-devel' 'git' 'jq' #1.4
prepare_zfsbootmenu_efi_bin_pkg #1.5
install_zfs #1.6
setup_zpool #1.7
mount_system #1.8
copy_zpool_cache #1.9
install_archlinux #1.10
gen_fstab #1.11
set_hostname #1.12
set_locale #1.13
add_zfs_hook_to_initramfs #1.14
set_initramfs_build_list #1.15
add_zfs_files_to_new_os #1.16
enter_chroot #2.1
# We're done in chroot
finalize_os_setup #3.1
fi
}
main