Compare commits
	
		
			2 Commits
		
	
	
		
			6f1efef36e
			...
			8bc0c2e8c3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8bc0c2e8c3 | |||
| 0c21e961b1 | 
							
								
								
									
										48
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								README.md
									
									
									
									
									
								
							@@ -1,3 +1,49 @@
 | 
			
		||||
# arch-zbm
 | 
			
		||||
 | 
			
		||||
Helper script to install Arch Linux with ZFSBootMenu
 | 
			
		||||
Helper script to install Arch Linux with ZFSBootMenu from within a running Arch live CD ISO image
 | 
			
		||||
 | 
			
		||||
# Prep
 | 
			
		||||
 | 
			
		||||
The script expects minimal prep on your end. Please make sure that before execution at least one of the following conditions are met.
 | 
			
		||||
 | 
			
		||||
- Your machine has exactly one partition with partition type code `BF00` ("Solaris root")
 | 
			
		||||
 | 
			
		||||
# How to run this?
 | 
			
		||||
 | 
			
		||||
- Boot an Arch Linux live CD ISO image
 | 
			
		||||
- Run:
 | 
			
		||||
    ```
 | 
			
		||||
    curl -s https://quico.space/hygienic-books/config-jetbrains-ides/raw/branch/master/colors.scheme.xml | bash
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
# Steps
 | 
			
		||||
 | 
			
		||||
The scripts takes the following installation steps.
 | 
			
		||||
 | 
			
		||||
1. Install ZFS with [github.com/eoli3n/archiso-zfs](https://github.com/eoli3n/archiso-zfs)
 | 
			
		||||
 | 
			
		||||
# Assumptions
 | 
			
		||||
 | 
			
		||||
# Development
 | 
			
		||||
 | 
			
		||||
## Conventional commits
 | 
			
		||||
 | 
			
		||||
This project uses [Conventional Commits](https://www.conventionalcommits.org/) for its commit messages.
 | 
			
		||||
 | 
			
		||||
### Commit types
 | 
			
		||||
 | 
			
		||||
Commit _types_ besides `fix` and `feat` are:
 | 
			
		||||
 | 
			
		||||
* `build`: Project structure, directory layout, build instructions for roll-out
 | 
			
		||||
* `refactor`: Keeping functionality while streamlining or otherwise improving function flow
 | 
			
		||||
* `test`: Working on test coverage
 | 
			
		||||
* `docs`: Documentation for project or components
 | 
			
		||||
 | 
			
		||||
### Commit scopes
 | 
			
		||||
 | 
			
		||||
The following _scopes_ are known for this project. A Conventional Commits commit message may optionally use one of the following scopes or none:
 | 
			
		||||
 | 
			
		||||
* `zbm`: Adjusting ZFSBootMenu's behavior
 | 
			
		||||
* `zfs`: A change to how ZFS interacts with the system, either a pool or a dataset
 | 
			
		||||
* `os`: Getting an perating system set up to correctly work in a ZFS boot environment
 | 
			
		||||
* `meta`: Affects the project's repo layout, readme content, file names etc.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										496
									
								
								setup.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										496
									
								
								setup.sh
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,496 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
declare this_script_remote_repo_raw_url zpool_name zfs_arch_dataset_name
 | 
			
		||||
this_script_remote_repo_raw_url='https://quico.space/...'
 | 
			
		||||
zpool_name='zpool'
 | 
			
		||||
zfs_arch_dataset_name='archlinux'
 | 
			
		||||
 | 
			
		||||
function set_ntp () {
 | 
			
		||||
    timedatectl set-ntp true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function we_are_changerooted () {
 | 
			
		||||
    if [ "$(stat -c '%d:%i' '/')" != "$(stat -c '%d:%i' '/proc/1/root/.')" ]; then
 | 
			
		||||
        return 0
 | 
			
		||||
    else
 | 
			
		||||
        return 1
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function install_pkgs () {
 | 
			
		||||
    printf -- '%s\n' 'Installing packages ...'
 | 
			
		||||
    pacman -S --needed --noconfirm "${@}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function install_zfs () {
 | 
			
		||||
    curl -s 'https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init' | bash
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_partitions () {
 | 
			
		||||
    declare partitions_json
 | 
			
		||||
    partitions_json="$(lsblk --output PATH,PARTTYPE --json)" || return 1
 | 
			
		||||
    printf -- '%s' "${partitions_json}"
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_parts () {
 | 
			
		||||
    declare parttype parts
 | 
			
		||||
    parttype="${1:?}"
 | 
			
		||||
    case "${parttype}" in
 | 
			
		||||
        zfs)
 | 
			
		||||
            parts="$(get_partitions | jq --raw-output '.[][] | select(.parttype=="6a85cf4d-1dd2-11b2-99a6-080020736631") | .path')" || exit 1
 | 
			
		||||
            ;;
 | 
			
		||||
        efi)
 | 
			
		||||
            parts="$(get_partitions | jq --raw-output '.[][] | select(.parttype=="c12a7328-f81f-11d2-ba4b-00a0c93ec93b") | .path')" || exit 1
 | 
			
		||||
            ;;
 | 
			
		||||
        *)
 | 
			
		||||
            printf -- '%s\n' 'Unknown partition type '"'"'"${parttype}"'"'"', exiting 1 ...'
 | 
			
		||||
            exit 1
 | 
			
		||||
            ;;
 | 
			
		||||
    esac
 | 
			
		||||
    printf -- '%s' "${parts}"
 | 
			
		||||
    return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function we_have_exactly_one_part () {
 | 
			
		||||
    declare 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)
 | 
			
		||||
                printf -- '%s\n' 'No '"${parttype}"' partition found. Exiting 1 ...'
 | 
			
		||||
                exit 1
 | 
			
		||||
                ;;
 | 
			
		||||
            1)
 | 
			
		||||
                return 0
 | 
			
		||||
                ;;
 | 
			
		||||
            *)
 | 
			
		||||
                printf -- '%s\n' 'Multiple '"${parttype}"' partitions found. We expect exactly one. Cowardly exiting 1 ...'
 | 
			
		||||
                exit 1
 | 
			
		||||
                ;;
 | 
			
		||||
        esac
 | 
			
		||||
        printf -- '%s\n' 'Partition list does not look valid. Cowardly exiting 1 ...'
 | 
			
		||||
        exit 1
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function no_zpool_exists () {
 | 
			
		||||
    declare zpool_list
 | 
			
		||||
    zpool_list="$(zpool list -H)"
 | 
			
		||||
    [[ "$(wc -l <<<"${zpool_list}")" -eq '0' ]] && return 0
 | 
			
		||||
    return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function zpool_drive_id () {
 | 
			
		||||
    declare drive_id
 | 
			
		||||
    drive_id="$(find -L /dev/disk/by-id -samefile "${1:?}")"
 | 
			
		||||
    if [[ "$(wc -l <<<"${drive_id}")" -eq '1' ]]; then
 | 
			
		||||
        printf -- '%s' "${drive_id}"
 | 
			
		||||
        return 0
 | 
			
		||||
    fi
 | 
			
		||||
    printf -- '%s\n' 'More than zpool partition entry in /dev/disk/by-id, exiting 1 ...'
 | 
			
		||||
    exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_zpool_password () {
 | 
			
		||||
    printf -- '%s\n' 'password' > '/etc/zfs/'"${zpool_name}"'.key'
 | 
			
		||||
    chmod '000' '/etc/zfs/'"${zpool_name}"'.key'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function import_pool () {
 | 
			
		||||
    zpool import -d '/dev/disk/by-id' -R '/mnt' "${zpool_name}" -N -f
 | 
			
		||||
    zfs load-key "${zpool_name}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function create_pool () {
 | 
			
		||||
    # Create a temporary pool that is not cached
 | 
			
		||||
    zpool create -f                                                     \
 | 
			
		||||
                 -o 'ashift=12'                                         \
 | 
			
		||||
                 -o 'autotrim=on'                                       \
 | 
			
		||||
                 -O 'acltype=posix'                                     \
 | 
			
		||||
                 -O 'compression=on'                                    \
 | 
			
		||||
                 -O 'relatime=on'                                       \
 | 
			
		||||
                 -O 'xattr=sa'                                          \
 | 
			
		||||
                 -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 () {
 | 
			
		||||
    declare zpool_drive drive_by_id
 | 
			
		||||
    zpool_drive="${1:?}"
 | 
			
		||||
    drive_by_id="$(zpool_drive_id "${zpool_drive}")"
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mount_system () {
 | 
			
		||||
    zfs mount "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
 | 
			
		||||
    zfs mount -a
 | 
			
		||||
    
 | 
			
		||||
    declare efi_part
 | 
			
		||||
    efi_part="$(get_parts 'efi')"
 | 
			
		||||
    if we_have_exactly_one_part 'efi' "${efi_part}"; then
 | 
			
		||||
        mkdir -p '/mnt/efi'
 | 
			
		||||
        mount "${efi_part}" '/mnt/efi'
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function copy_zpool_cache () {
 | 
			
		||||
    mkdir -p '/mnt/etc/zfs'
 | 
			
		||||
    zpool set 'cachefile=/etc/zfs/'"${zpool_name}"'.cache' "${zpool_name}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function install_archlinux () {
 | 
			
		||||
    sed -ri -e 's'$'\x1''^.*?(ParallelDownloads)[^\r\n\f]*'$'\x1''\1 = 5'$'\x1''g' '/etc/pacman.conf'
 | 
			
		||||
    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 () {
 | 
			
		||||
    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 () {
 | 
			
		||||
    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 () {
 | 
			
		||||
    printf -- '%s\n' 'KEYMAP=de-latin1' > '/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 () {
 | 
			
		||||
    sed -ri -e 's'$'\x1''(HOOKS=)(.*?[\(| ])(filesystems)([\)| ][^\r\n\f]*)'$'\x1''\1\2zfs \3\4'$'\x1''g' '/mnt/etc/mkinitcpio.conf'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function add_zfs_files_to_new_os () {
 | 
			
		||||
    for zfs_file in '/etc/hostid' '/etc/zfs/zpool.cache' '/etc/zfs/zpool.key'; do
 | 
			
		||||
        rsync -av --itemize-changes {'','/mnt'}"${zfs_file}"
 | 
			
		||||
    done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enter_chroot () {
 | 
			
		||||
    arch-chroot /mnt /bin/bash -xe <<EOF
 | 
			
		||||
curl --silent '${this_script_remote_repo_raw_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 get_aur_helper () {
 | 
			
		||||
    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'
 | 
			
		||||
    chmod -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 () {
 | 
			
		||||
    sudo --user build paru -S --noconfirm "${@}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function install_os_in_chroot () {
 | 
			
		||||
    ### 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
 | 
			
		||||
 | 
			
		||||
    get_aur_helper
 | 
			
		||||
    paru_install 'zfs-dkms' 'zfs-utils'
 | 
			
		||||
    hwclock --systohc
 | 
			
		||||
    locale-gen
 | 
			
		||||
    source /etc/locale.conf
 | 
			
		||||
    mkinitcpio -P
 | 
			
		||||
 | 
			
		||||
    # Install ZFSBootMenu and deps
 | 
			
		||||
    git clone --depth=1 https://github.com/zbm-dev/zfsbootmenu/ '/tmp/zfsbootmenu'
 | 
			
		||||
    paru_install 'cpanminus' 'kexec-tools' 'fzf' 'util-linux'
 | 
			
		||||
    pushd '/tmp/zfsbootmenu'
 | 
			
		||||
    make
 | 
			
		||||
    make install
 | 
			
		||||
    cpanm --notest --installdeps .
 | 
			
		||||
    popd
 | 
			
		||||
    rm -rf '/tmp/zfsbootmenu'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function set_root_pw () {
 | 
			
		||||
    printf -- '%s\n' 'root:password' | chpasswd --root '/mnt'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function configure_networking () {
 | 
			
		||||
    cat > '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
 | 
			
		||||
[Match]
 | 
			
		||||
Name=en*
 | 
			
		||||
 | 
			
		||||
[Network]
 | 
			
		||||
DHCP=ipv4
 | 
			
		||||
IPForward=yes
 | 
			
		||||
 | 
			
		||||
[DHCPV4]
 | 
			
		||||
UseDNS=no
 | 
			
		||||
RouteMetric=10
 | 
			
		||||
EOF
 | 
			
		||||
    systemctl enable 'systemd-networkd' --root='/mnt'
 | 
			
		||||
    systemctl disable 'systemd-networkd-wait-online' --root='/mnt'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function configure_dns () {
 | 
			
		||||
    rm '/mnt/etc/resolv.conf'
 | 
			
		||||
    ln -s '/run/systemd/resolve/stub-resolv.conf' '/mnt/etc/resolv.conf'
 | 
			
		||||
    # sed -i 's/^#DNS=.*/DNS=1.1.1.1/' /mnt/etc/systemd/resolved.conf
 | 
			
		||||
    systemctl enable 'systemd-resolved' --root='/mnt'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function configure_zfs () {
 | 
			
		||||
    systemctl enable 'zfs-import-cache' 'zfs-mount' 'zfs-import.target' 'zfs.target' --root='/mnt'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function configure_zfs_mount_gen () {
 | 
			
		||||
    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 () {
 | 
			
		||||
    mkdir -p '/mnt/efi/EFI/ZBM'
 | 
			
		||||
    curl -s 'https://raw.githubusercontent.com/zbm-dev/zfsbootmenu/master/etc/zfsbootmenu/mkinitcpio.conf' | sed -r -e '/^#/d' -e '/^$/d' > '/mnt/etc/zfsbootmenu/mkinitcpio.conf'
 | 
			
		||||
    cat > '/mnt/etc/zfsbootmenu/config.yaml' <<EOF
 | 
			
		||||
Global:
 | 
			
		||||
  ManageImages: true
 | 
			
		||||
  BootMountPoint: /efi
 | 
			
		||||
  InitCPIO: true
 | 
			
		||||
 | 
			
		||||
Components:
 | 
			
		||||
  Enabled: false
 | 
			
		||||
EFI:
 | 
			
		||||
  ImageDir: /efi/EFI/ZBM
 | 
			
		||||
  Versions: false
 | 
			
		||||
  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 gen_zfsbootmenu () {
 | 
			
		||||
    arch-chroot /mnt /bin/bash -xe <<"EOF"
 | 
			
		||||
source /etc/locale.conf
 | 
			
		||||
find /efi/EFI/ZBM -type f -delete
 | 
			
		||||
generate-zbm
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_disk_with_efipart () {
 | 
			
		||||
    declare disks_with_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_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=="ebd0a0a2-b9e5-4433-87c0-68b6b72699c7")] | length == 1) ) | .path')"
 | 
			
		||||
    if [[ "$(wc -l <<<"${disks_with_efipart}")" -eq '1' ]] && [[ "$(wc -c <<<"${disks_with_efipart}")" -gt '1' ]]; then
 | 
			
		||||
        printf -- '%s' "${disks_with_efipart}"
 | 
			
		||||
        return 0
 | 
			
		||||
    fi
 | 
			
		||||
    return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function add_zbm_to_efi () {
 | 
			
		||||
    if ! efibootmgr | grep -Pi -- 'ZFSBootMenu'; then
 | 
			
		||||
        declare efi_disk
 | 
			
		||||
        efi_disk="$(get_disk_with_efipart)"
 | 
			
		||||
        efibootmgr --disk "${efi_disk}" \
 | 
			
		||||
          --part 1 \
 | 
			
		||||
          --create \
 | 
			
		||||
          --label "ZFSBootMenu" \
 | 
			
		||||
          --loader "\EFI\ZBM\vmlinuz.efi" \
 | 
			
		||||
          --verbose
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function umount_all () {
 | 
			
		||||
    umount '/mnt/efi'
 | 
			
		||||
    zfs umount -a
 | 
			
		||||
    zpool export "${zpool_name}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function finalize_os_setup () {
 | 
			
		||||
    set_root_pw
 | 
			
		||||
    configure_networking
 | 
			
		||||
    configure_dns
 | 
			
		||||
    configure_zfs
 | 
			
		||||
    configure_zfs_mount_gen
 | 
			
		||||
    configure_zfsbootmenu
 | 
			
		||||
    gen_zfsbootmenu
 | 
			
		||||
    add_zbm_to_efi
 | 
			
		||||
    umount_all
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function main () {
 | 
			
		||||
    if we_are_changerooted; then
 | 
			
		||||
        install_os_in_chroot
 | 
			
		||||
    else
 | 
			
		||||
        set_ntp
 | 
			
		||||
        install_pkgs 'jq'
 | 
			
		||||
        declare zfs_part
 | 
			
		||||
        zfs_part="$(get_parts 'zfs')"
 | 
			
		||||
        if we_have_exactly_one_part 'zfs' "${zfs_part}"; then
 | 
			
		||||
            printf -- 'Creating zpool on partition '"'"'%s'"'"' ...\n' "${zfs_part}"
 | 
			
		||||
            install_zfs
 | 
			
		||||
            setup_zpool "${zfs_part}"
 | 
			
		||||
            mount_system
 | 
			
		||||
            copy_zpool_cache
 | 
			
		||||
            systemctl start reflector
 | 
			
		||||
            install_archlinux
 | 
			
		||||
            gen_fstab
 | 
			
		||||
            set_hostname
 | 
			
		||||
            set_locale
 | 
			
		||||
            add_zfs_hook_to_initramfs
 | 
			
		||||
            enter_chroot
 | 
			
		||||
            # We're done in chroot
 | 
			
		||||
            finalize_os_setup
 | 
			
		||||
        fi
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main
 | 
			
		||||
		Reference in New Issue
	
	Block a user