Compare commits
	
		
			7 Commits
		
	
	
		
			b36d4d33c3
			...
			d1407f00a9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d1407f00a9 | |||
| 767e9c3b42 | |||
| 915ded9c1b | |||
| 598b176ec6 | |||
| dcc3cf7d93 | |||
| be63ed90ad | |||
| abffd08c1e | 
							
								
								
									
										331
									
								
								setup.sh
									
									
									
									
									
								
							
							
						
						
									
										331
									
								
								setup.sh
									
									
									
									
									
								
							@@ -18,6 +18,7 @@ exec 3>&1
 | 
			
		||||
 | 
			
		||||
declare this_script_url
 | 
			
		||||
this_script_url="${SCRIPT_URL:?}"
 | 
			
		||||
postconf_hook="$(dirname "${this_script_url}")"'/zbm_set_new_uefi_boot_entries.sh'
 | 
			
		||||
 | 
			
		||||
declare zpool_name zfs_arch_dataset_name
 | 
			
		||||
zpool_name='zpool'
 | 
			
		||||
@@ -282,68 +283,12 @@ function mount_system () {
 | 
			
		||||
    mount "${efi_part}" '/mnt/efi'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function prepare_zfsbootmenu_efi_bin_pkg () {
 | 
			
		||||
    #1.8
 | 
			
		||||
    # 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 || {
 | 
			
		||||
        >&3 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}" || {
 | 
			
		||||
        >&3 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 copy_zpool_cache () {
 | 
			
		||||
    #1.9
 | 
			
		||||
    #1.8
 | 
			
		||||
    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
 | 
			
		||||
@@ -359,7 +304,7 @@ function pacman_dont_check_space () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function install_archlinux () {
 | 
			
		||||
    #1.10
 | 
			
		||||
    #1.9
 | 
			
		||||
    pacman_dl_parallel
 | 
			
		||||
    pacman_dont_check_space
 | 
			
		||||
    pacstrap /mnt              \
 | 
			
		||||
@@ -386,7 +331,7 @@ function install_archlinux () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function gen_fstab () {
 | 
			
		||||
    #1.11
 | 
			
		||||
    #1.10
 | 
			
		||||
    genfstab -U /mnt | grep -v "${zpool_name}" | tr -s '\n' | sed -r -e 's/\/mnt//' -e '/./,$!d' > '/mnt/etc/fstab'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -399,7 +344,7 @@ EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_hostname () {
 | 
			
		||||
    #1.12
 | 
			
		||||
    #1.11
 | 
			
		||||
    declare new_hostname
 | 
			
		||||
    install_pkgs 'pwgen'
 | 
			
		||||
    new_hostname="$(pwgen --no-numerals --no-capitalize --ambiguous 8)"
 | 
			
		||||
@@ -408,7 +353,7 @@ function set_hostname () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_locale () {
 | 
			
		||||
    #1.13
 | 
			
		||||
    #1.12
 | 
			
		||||
    printf -- '%s\n' \
 | 
			
		||||
        'KEYMAP=de-latin1' \
 | 
			
		||||
        'FONT=Lat2-Terminus16' \
 | 
			
		||||
@@ -419,7 +364,7 @@ function set_locale () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function add_zfs_hook_to_initramfs () {
 | 
			
		||||
    #1.14
 | 
			
		||||
    #1.13
 | 
			
		||||
    # 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' \
 | 
			
		||||
@@ -434,7 +379,7 @@ function add_zfs_hook_to_initramfs () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_initramfs_build_list () {
 | 
			
		||||
    #1.15
 | 
			
		||||
    #1.14
 | 
			
		||||
    # No need to build fallback initramfs, our new fallback is ZFS snapshots
 | 
			
		||||
    sed -ri \
 | 
			
		||||
        -e '/^#/d' \
 | 
			
		||||
@@ -448,19 +393,12 @@ function set_initramfs_build_list () {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function add_zfs_files_to_new_os () {
 | 
			
		||||
    #1.16
 | 
			
		||||
    #1.15
 | 
			
		||||
    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 create_unpriv_user () {
 | 
			
		||||
    account_name="${1:?}"
 | 
			
		||||
    full_name="${2:-${account_name}}"
 | 
			
		||||
@@ -472,6 +410,52 @@ function create_unpriv_user () {
 | 
			
		||||
    chown -R "${account_name}"':' '/home/'"${account_name}"; chmod -R 'u=rwX,go=' "$(dirname "${authorized_keys_abs_path}")"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enter_chroot () {
 | 
			
		||||
    #2.1
 | 
			
		||||
    arch-chroot /mnt /bin/bash -xe <<EOF
 | 
			
		||||
curl --silent '${this_script_url}' | bash
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 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 unleash_makepkg () {
 | 
			
		||||
    #2.5
 | 
			
		||||
    local path_prefix
 | 
			
		||||
@@ -484,6 +468,25 @@ function unleash_makepkg () {
 | 
			
		||||
        "${path_prefix}"'/etc/makepkg.conf'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    useradd --create-home --shell /bin/bash --user-group --groups wheel <user>
 | 
			
		||||
    passwd <user>
 | 
			
		||||
    systemctl enable --now sddm.service
 | 
			
		||||
 | 
			
		||||
####################
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_aur_helper () {
 | 
			
		||||
    #2.7
 | 
			
		||||
    create_unpriv_user 'build'
 | 
			
		||||
@@ -542,65 +545,41 @@ function paru_install () {
 | 
			
		||||
    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 configure_zfsbootmenu () {
 | 
			
		||||
    #2.9
 | 
			
		||||
    paru_install 'zfsbootmenu'
 | 
			
		||||
    mkdir -p '/etc/zfsbootmenu/posthooks.d'
 | 
			
		||||
    cat > '/etc/zfsbootmenu/config.yaml' <<EOF
 | 
			
		||||
Global:
 | 
			
		||||
  ManageImages: true
 | 
			
		||||
  BootMountPoint: /efi
 | 
			
		||||
  InitCPIO: true
 | 
			
		||||
  PostHooksDir: /etc/zfsbootmenu/posthooks.d
 | 
			
		||||
Components:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
EFI:
 | 
			
		||||
  ImageDir: /efi/EFI/ZBM
 | 
			
		||||
  Versions: 1
 | 
			
		||||
  Enabled: true
 | 
			
		||||
Kernel:
 | 
			
		||||
  CommandLine: ro loglevel=0 zbm.import_policy=hostid
 | 
			
		||||
  Prefix: vmlinuz
 | 
			
		||||
EOF
 | 
			
		||||
# Up here maybe 'ro quiet' instead of 'ro'
 | 
			
		||||
    zfs set org.zfsbootmenu:commandline='rw nowatchdog rd.vconsole.keymap=de-latin1' "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    useradd --create-home --shell /bin/bash --user-group --groups wheel <user>
 | 
			
		||||
    passwd <user>
 | 
			
		||||
    systemctl enable --now sddm.service
 | 
			
		||||
 | 
			
		||||
####################
 | 
			
		||||
 | 
			
		||||
EOF
 | 
			
		||||
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 install_os_in_chroot () {
 | 
			
		||||
@@ -623,12 +602,13 @@ function install_os_in_chroot () {
 | 
			
		||||
    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'
 | 
			
		||||
    paru_install 'zfs-dkms' 'zfs-utils' 'jq'
 | 
			
		||||
    hwclock --systohc
 | 
			
		||||
    mkinitcpio -P
 | 
			
		||||
 | 
			
		||||
    # Install ZFSBootMenu image
 | 
			
		||||
    install_zbm_image                           #2.8
 | 
			
		||||
    configure_zfsbootmenu                       #2.9
 | 
			
		||||
    generate-zbm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_root_pw () {
 | 
			
		||||
@@ -683,47 +663,57 @@ function configure_zfs_mount_gen () {
 | 
			
		||||
    systemctl enable 'zfs-zed.service' --root='/mnt'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function configure_zfsbootmenu () {
 | 
			
		||||
function set_new_uefi_boot_entries () {
 | 
			
		||||
    #3.8
 | 
			
		||||
    zfs set org.zfsbootmenu:commandline='rw nowatchdog rd.vconsole.keymap=de-latin1' "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
 | 
			
		||||
}
 | 
			
		||||
    declare -a uefi_images
 | 
			
		||||
    mapfile -t uefi_images < \
 | 
			
		||||
        <(find '/mnt/efi/EFI/ZBM' -type f -iname '*.efi' -print0 | \
 | 
			
		||||
        xargs -0 --no-run-if-empty --max-args '1' stat -c '%Y %n' | \
 | 
			
		||||
        sort -V | \
 | 
			
		||||
        awk '{print $2}')
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    if efibootmgr | grep -Piq -- 'ZFSBootMenu'; then
 | 
			
		||||
        local -a old_uefi_entries
 | 
			
		||||
        mapfile -t old_uefi_entries < \
 | 
			
		||||
            <(efibootmgr | \
 | 
			
		||||
            grep -Pio -- '(?<=^Boot)[^\*[:space:]]+(?=\*? ZFSBootMenu)')
 | 
			
		||||
        for old_uefi_entry in "${old_uefi_entries[@]}"; do
 | 
			
		||||
            efibootmgr --bootnum "${old_uefi_entry}" --delete-bootnum &>/dev/null && {
 | 
			
		||||
                >&3 printf -- '%s\n' \
 | 
			
		||||
                    'EFI boot entry '"${old_uefi_entry}"' deleted.'
 | 
			
		||||
            }
 | 
			
		||||
        done
 | 
			
		||||
    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 recovery" \
 | 
			
		||||
              --loader "\EFI\zbm\zfsbootmenu-recovery-vmlinuz-x86_64.EFI" \
 | 
			
		||||
              --verbose
 | 
			
		||||
            efibootmgr --disk "${efi_drive}" \
 | 
			
		||||
              --part 1 \
 | 
			
		||||
              --create \
 | 
			
		||||
              --label "ZFSBootMenu release" \
 | 
			
		||||
              --loader "\EFI\zbm\zfsbootmenu-release-vmlinuz-x86_64.EFI" \
 | 
			
		||||
              --verbose
 | 
			
		||||
            for uefi_image in "${uefi_images[@]}"; do
 | 
			
		||||
                uefi_image_version="$(basename "${uefi_image##*-}")"
 | 
			
		||||
                uefi_image_version="${uefi_image_version%%.EFI}"
 | 
			
		||||
                uefi_image_inverted="${uefi_image#/mnt/efi}"
 | 
			
		||||
                uefi_image_inverted="${uefi_image_inverted//\//\\}"
 | 
			
		||||
                efibootmgr --disk "${efi_drive}" \
 | 
			
		||||
                    --part 1 \
 | 
			
		||||
                    --create \
 | 
			
		||||
                    --label 'ZFSBootMenu '"${uefi_image_version}" \
 | 
			
		||||
                    --loader "${uefi_image_inverted}" &>/dev/null && {
 | 
			
		||||
                        >&3 printf -- '%s\n' \
 | 
			
		||||
                            'EFI boot entry ZFSBootMenu '"${uefi_image_version}"' added.'
 | 
			
		||||
            }
 | 
			
		||||
            done
 | 
			
		||||
        fi
 | 
			
		||||
        efibootmgr
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function insert_zbm_postconf_hook () {
 | 
			
		||||
    #3.9
 | 
			
		||||
    declare postconf_target_abs='/mnt/etc/zfsbootmenu/posthooks.d/'"$(basename "${postconf_hook}")"
 | 
			
		||||
    curl --silent --location "${postconf_hook}" --output "${postconf_target_abs}"
 | 
			
		||||
    chmod +x "${postconf_target_abs}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function umount_all () {
 | 
			
		||||
    #3.10
 | 
			
		||||
    umount '/mnt/efi'
 | 
			
		||||
@@ -739,9 +729,9 @@ function finalize_os_setup () {
 | 
			
		||||
    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
 | 
			
		||||
    set_new_uefi_boot_entries                   #3.8
 | 
			
		||||
    insert_zbm_postconf_hook                    #3.9
 | 
			
		||||
    umount_all                                  #3.10
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function main () {
 | 
			
		||||
@@ -755,15 +745,14 @@ function main () {
 | 
			
		||||
        install_zfs                             #1.5
 | 
			
		||||
        setup_zpool                             #1.6
 | 
			
		||||
        mount_system                            #1.7
 | 
			
		||||
        prepare_zfsbootmenu_efi_bin_pkg         #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
 | 
			
		||||
        copy_zpool_cache                        #1.8
 | 
			
		||||
        install_archlinux                       #1.9
 | 
			
		||||
        gen_fstab                               #1.10
 | 
			
		||||
        set_hostname                            #1.11
 | 
			
		||||
        set_locale                              #1.12
 | 
			
		||||
        add_zfs_hook_to_initramfs               #1.13
 | 
			
		||||
        set_initramfs_build_list                #1.14
 | 
			
		||||
        add_zfs_files_to_new_os                 #1.15
 | 
			
		||||
        enter_chroot                            #2.1
 | 
			
		||||
        # We're done in chroot
 | 
			
		||||
        finalize_os_setup                       #3.1
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										177
									
								
								zbm_set_new_uefi_boot_entries.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								zbm_set_new_uefi_boot_entries.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,177 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
# Whatever comes in on file descriptor (FD) 3 gets redirected to where file
 | 
			
		||||
# descriptor 1 is pointing. File descriptor 1 points to stdout so when we
 | 
			
		||||
# output-redirect something into FD 3 it shows up on stdout. We can use this
 | 
			
		||||
# to produce arbitrary logging output inside a subshell like so:
 | 
			
		||||
#
 | 
			
		||||
# function my_func () {
 | 
			
		||||
#     some_command "${1:?}"
 | 
			
		||||
#     >&3 echo 'A log message'
 | 
			
		||||
# }
 | 
			
		||||
#
 | 
			
		||||
# var="$(my_func arg_1)"
 | 
			
		||||
#
 | 
			
		||||
# Here "${var}" will only capture the output of some_command "${1:?}". It
 | 
			
		||||
# will not capture 'echo' which will instead show up on our stdout/FD 1.
 | 
			
		||||
exec 3>&1
 | 
			
		||||
 | 
			
		||||
# https://unix.stackexchange.com/a/48550
 | 
			
		||||
set -E
 | 
			
		||||
trap '[ "$?" -ne 77 ] || exit 77' ERR
 | 
			
		||||
 | 
			
		||||
function get_partitions () {
 | 
			
		||||
    declare partitions_json
 | 
			
		||||
    partitions_json="$(lsblk --output PATH,PARTTYPE --json --tree)" || return 1
 | 
			
		||||
    printf -- '%s' "${partitions_json}"
 | 
			
		||||
    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
 | 
			
		||||
            ;;
 | 
			
		||||
        *)
 | 
			
		||||
            >&3 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)
 | 
			
		||||
                >&3 printf -- '%s\n' 'No '"${parttype^^}"' partition found. Exiting ...'
 | 
			
		||||
                exit 77
 | 
			
		||||
                ;;
 | 
			
		||||
            1)
 | 
			
		||||
                return 0
 | 
			
		||||
                ;;
 | 
			
		||||
            *)
 | 
			
		||||
                return 1
 | 
			
		||||
                ;;
 | 
			
		||||
        esac
 | 
			
		||||
        >&3 printf -- '%s\n' 'Partition list does not look valid. Cowardly exiting ...'
 | 
			
		||||
        exit 77
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function select_part () {
 | 
			
		||||
    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 [[ ! "${parts}" ]]; then
 | 
			
		||||
        case "${part_type}" in
 | 
			
		||||
            efi)
 | 
			
		||||
                part_type_human_readable='EFI system partition (ESP) with partition type code EF00'
 | 
			
		||||
                ;;
 | 
			
		||||
            zfs)
 | 
			
		||||
                part_type_human_readable='ZFS zpool partition with partition type code BF00'
 | 
			
		||||
                ;;
 | 
			
		||||
        esac
 | 
			
		||||
        >&3 printf -- '%s\n' \
 | 
			
		||||
            'It looks as if there is no '"${part_type_human_readable}" \
 | 
			
		||||
            'on any of the disks. Did you correctly partition a disk before starting?' \
 | 
			
		||||
            'Check https://quico.space/quico-os-setup/arch-zbm#prep. Exiting ...'
 | 
			
		||||
        exit 77
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    if we_have_exactly_one_part "${part_type}" "${parts}"; then
 | 
			
		||||
        part="${parts}"
 | 
			
		||||
    else
 | 
			
		||||
        >&3 printf -- '%s\n' 'More than one '"${part_type^^}"' partition to pick for installation. Cowardly exiting ...'
 | 
			
		||||
        exit 77
 | 
			
		||||
    fi
 | 
			
		||||
    printf -- '%s' "${part}"
 | 
			
		||||
    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_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 set_new_uefi_boot_entries () {
 | 
			
		||||
    declare -a uefi_images
 | 
			
		||||
    mapfile -t uefi_images < \
 | 
			
		||||
        <(find '/efi/EFI/ZBM' -type f -iname '*.efi' -print0 | \
 | 
			
		||||
        xargs -0 --no-run-if-empty --max-args '1' stat -c '%Y %n' | \
 | 
			
		||||
        sort -V | \
 | 
			
		||||
        awk '{print $2}')
 | 
			
		||||
    zpool_drive="$(select_part 'zfs')"
 | 
			
		||||
    zfs_parent="$(get_part_parent "${zpool_drive:?}")"
 | 
			
		||||
    efi_drive="${zfs_parent}"
 | 
			
		||||
 | 
			
		||||
    if efibootmgr | grep -Piq -- 'ZFSBootMenu'; then
 | 
			
		||||
        local -a old_uefi_entries
 | 
			
		||||
        mapfile -t old_uefi_entries < \
 | 
			
		||||
            <(efibootmgr | \
 | 
			
		||||
            grep -Pio -- '(?<=^Boot)[^\*[:space:]]+(?=\*? ZFSBootMenu)')
 | 
			
		||||
        for old_uefi_entry in "${old_uefi_entries[@]}"; do
 | 
			
		||||
            efibootmgr --bootnum "${old_uefi_entry}" --delete-bootnum &>/dev/null && {
 | 
			
		||||
                >&3 printf -- '%s\n' \
 | 
			
		||||
                    'EFI boot entry '"${old_uefi_entry}"' deleted.'
 | 
			
		||||
            }
 | 
			
		||||
        done
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
            for uefi_image in "${uefi_images[@]}"; do
 | 
			
		||||
                uefi_image_version="$(basename "${uefi_image##*-}")"
 | 
			
		||||
                uefi_image_version="${uefi_image_version%%.EFI}"
 | 
			
		||||
                uefi_image_inverted="${uefi_image#/mnt/efi}"
 | 
			
		||||
                uefi_image_inverted="${uefi_image_inverted//\//\\}"
 | 
			
		||||
                efibootmgr --disk "${efi_drive}" \
 | 
			
		||||
                    --part 1 \
 | 
			
		||||
                    --create \
 | 
			
		||||
                    --label 'ZFSBootMenu '"${uefi_image_version}" \
 | 
			
		||||
                    --loader "${uefi_image_inverted}" &>/dev/null && {
 | 
			
		||||
                        >&3 printf -- '%s\n' \
 | 
			
		||||
                            'EFI boot entry ZFSBootMenu '"${uefi_image_version}"' added.'
 | 
			
		||||
            }
 | 
			
		||||
            done
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user