In situations where this script runs on alternative Arch Linux live CD ISOs such as github.com/stevleibelt/arch-linux-live-cd-iso-with -zfs we may not have to insall ZFS kernel modules. Test if the 'zfs' module is loaded and skip installation if yes.
1591 lines
56 KiB
Bash
1591 lines
56 KiB
Bash
#!/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
|
|
|
|
declare this_script_url
|
|
this_script_url="${SCRIPT_URL:?}"
|
|
|
|
declare zpool_name zfs_arch_dataset_name settings_file
|
|
zpool_name='zpool'
|
|
zfs_arch_dataset_name='archlinux'
|
|
settings_file='archzbm_settings.env'
|
|
|
|
declare -A partition_types
|
|
partition_types[gpt_zfs]='6a85cf4d-1dd2-11b2-99a6-080020736631'
|
|
partition_types[gpt_efi]='c12a7328-f81f-11d2-ba4b-00a0c93ec93b'
|
|
partition_types[mbr_zfs]='0xbf'
|
|
partition_types[mbr_boot]='0x83'
|
|
|
|
# https://unix.stackexchange.com/a/48550
|
|
set -E
|
|
trap '[ "$?" -ne 77 ] || exit 77' ERR
|
|
|
|
declare zpool_drive efi_drive boot_drive part_schema
|
|
|
|
function calculate_prefix_from_netmask () {
|
|
# https://stackoverflow.com/a/50419919
|
|
c='0'
|
|
x='0'"$(printf -- '%o' ${1//./ })"
|
|
while [ "${x}" -gt '0' ]; do
|
|
# Modulo then bitwise-shift x and store as new x
|
|
let c+="$(( x % 2 ))" 'x>>=1'
|
|
done
|
|
printf -- '%s' '/'"${c}";
|
|
}
|
|
|
|
function setup_env_vars () {
|
|
printf -- '%s\n' \
|
|
'We will go over a series of questions to create an answer file with' \
|
|
'options you want the script to use.' \
|
|
'' \
|
|
'Current working directory is:'\
|
|
"$(pwd)" \
|
|
'' \
|
|
'After we'"'"'re done answer file will be written to current working dir:' \
|
|
'./'"${settings_file}" \
|
|
'' \
|
|
'Press <Ctrl>+C to abort this process. No answer file will' \
|
|
'be written to ./'"${settings_file}"' if you abort the script.' \
|
|
'' \
|
|
'When done rerun the same command you just did without '"'"'setup'"'"' argument.' \
|
|
''
|
|
read -u3 -n 1 -s -r -p "Press any key to begin questionnaire"
|
|
echo
|
|
echo '----------------------------------------'
|
|
echo
|
|
|
|
read -u3 -p 'Please type kernel version to use, leave empty for latest, confirm with <Enter>: ' ARCHZBM_KERNEL_VER
|
|
echo
|
|
|
|
echo 'Do you want compressed datasets?'
|
|
select arg_compressed in 'Compressed' 'Uncompressed'; do
|
|
case "${arg_compressed}" in
|
|
'Compressed')
|
|
break
|
|
;;
|
|
'Uncompressed')
|
|
ARCHZBM_ZFSPROPS_NO_COMPRESSION='true'
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
echo 'Do you want encrypted datasets?'
|
|
select arg_encrypted in 'Encrypted' 'Unencrypted'; do
|
|
case "${arg_encrypted}" in
|
|
'Encrypted')
|
|
break
|
|
;;
|
|
'Unencrypted')
|
|
ARCHZBM_ZFSPROPS_NO_ENCRYPTION='true'
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${arg_encrypted}" = 'Encrypted' ]]; then
|
|
echo 'Do you want a custom dataset decryption password?'
|
|
select arg_custom_dataset_pw in 'Yes' 'No (use '"'"'password'"'"')'; do
|
|
case "${arg_custom_dataset_pw}" in
|
|
'Yes')
|
|
want_custom_dataset_pw='true'
|
|
break
|
|
;;
|
|
'No (use '"'"'password'"'"')')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_custom_dataset_pw}" ]]; then
|
|
read -u3 -p 'Please type password, confirm with <Enter>: ' -s ARCHZBM_ZPOOL_PASSWORD
|
|
echo
|
|
echo
|
|
fi
|
|
fi
|
|
|
|
echo 'Do you want a custom '"'"'root'"'"' user password?'
|
|
select arg_custom_root_pw in 'Yes' 'No (use '"'"'password'"'"')'; do
|
|
case "${arg_custom_root_pw}" in
|
|
'Yes')
|
|
want_custom_root_pw='true'
|
|
break
|
|
;;
|
|
'No (use '"'"'password'"'"')')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_custom_root_pw}" ]]; then
|
|
read -u3 -p 'Please type password, confirm with <Enter>: ' -s ARCHZBM_ROOT_PASSWORD
|
|
echo
|
|
echo
|
|
fi
|
|
|
|
echo 'Do you want an SSH daemon in ZFSBootMenu?'
|
|
select arg_ssh_in_zbm in "Yes" "No"; do
|
|
case "${arg_ssh_in_zbm}" in
|
|
Yes)
|
|
want_ssh_in_zbm='true'
|
|
break
|
|
;;
|
|
No)
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_ssh_in_zbm}" ]]; then
|
|
echo 'How do you want to assign an IP address in ZFSBootMenu?'
|
|
select arg_ip_autoconf_method in 'Statically' 'Dynamically, DHCP' 'Dynamically, BOOTP' 'Dynamically, RARP'; do
|
|
case "${arg_ip_autoconf_method}" in
|
|
'Statically')
|
|
ARCHZBM_NET_AUTOCONF='none'
|
|
break
|
|
;;
|
|
'Dynamically, DHCP')
|
|
ARCHZBM_NET_AUTOCONF='dhcp'
|
|
break
|
|
;;
|
|
'Dynamically, BOOTP')
|
|
ARCHZBM_NET_AUTOCONF='bootp'
|
|
break
|
|
;;
|
|
'Dynamically, RARP')
|
|
ARCHZBM_NET_AUTOCONF='rarp'
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
read -u3 -p 'Which device to configure (eth0 is a good guess): ' ARCHZBM_NET_DEVICE
|
|
echo
|
|
|
|
if [[ "${arg_ip_autoconf_method}" = 'Statically' ]]; then
|
|
read -u3 -p 'Interface IP address: ' ARCHZBM_NET_CLIENT_IP
|
|
echo
|
|
|
|
read -u3 -p 'Netmask: ' ARCHZBM_NET_NETMASK
|
|
echo
|
|
|
|
read -u3 -p 'Gateway IP address: ' ARCHZBM_NET_GATEWAY_IP
|
|
echo
|
|
fi
|
|
|
|
echo 'Do you want a custom SSH listening port?'
|
|
select arg_custom_ssh_port in 'Yes (let me specify)' 'No (keep port 22)'; do
|
|
case "${arg_custom_ssh_port}" in
|
|
'Yes (let me specify)')
|
|
want_custom_ssh_port='true'
|
|
break
|
|
;;
|
|
'No (keep port 22)')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_custom_ssh_port}" ]]; then
|
|
read -u3 -p 'Please type SSH port, confirm with <Enter>: ' ARCHZBM_SSH_PORT
|
|
echo
|
|
fi
|
|
|
|
echo 'Do you want the SSH daemon to use a custom keepalive send interval?'
|
|
select arg_custom_ssh_keepalive_intvl in 'Yes (let me specify)' 'No (keep 1)'; do
|
|
case "${arg_custom_ssh_keepalive_intvl}" in
|
|
'Yes (let me specify)')
|
|
want_custom_keepalive_intvl='true'
|
|
break
|
|
;;
|
|
'No (keep 1)')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_custom_keepalive_intvl}" ]]; then
|
|
read -u3 -p 'Please type server keepalive send interval, confirm with <Enter>: ' ARCHZBM_SSH_KEEPALIVE_INTVL
|
|
echo
|
|
fi
|
|
|
|
read -u3 -p 'Please type SSH pub keys on one line separated by double-commas (,,) and confirm with <Enter>: ' ARCHZBM_SSH_AUTH_KEYS
|
|
echo
|
|
fi
|
|
|
|
if [[ "${want_ssh_in_zbm}" ]]; then
|
|
echo 'Do you want to define operating system'"'"'s IP address?'
|
|
select arg_os_ip in 'Yes (let me specify)' 'Yes (use ZBM addresses)' 'No (DHCP is fine)'; do
|
|
case "${arg_os_ip}" in
|
|
'Yes (let me specify)')
|
|
want_custom_ip_in_os='true'
|
|
want_dns_and_ntp='true'
|
|
break
|
|
;;
|
|
'Yes (use ZBM addresses)')
|
|
ARCHZBM_OS_CLIENT_IP="${ARCHZBM_NET_CLIENT_IP}"
|
|
ARCHZBM_NET_CLIENT_IP_PREFIX="$(calculate_prefix_from_netmask "${ARCHZBM_NET_NETMASK}")"
|
|
ARCHZBM_OS_CLIENT_IP+="${ARCHZBM_NET_CLIENT_IP_PREFIX}"
|
|
ARCHZBM_OS_GATEWAY_IP="${ARCHZBM_NET_GATEWAY_IP}"
|
|
want_dns_and_ntp='true'
|
|
break
|
|
;;
|
|
'No (DHCP is fine)')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_custom_ip_in_os}" ]]; then
|
|
read -u3 -p 'Interface IP address with CIDR prefix (a.b.c.d/nn): ' ARCHZBM_OS_CLIENT_IP
|
|
echo
|
|
|
|
read -u3 -p 'Gateway IP address: ' ARCHZBM_OS_GATEWAY_IP
|
|
echo
|
|
fi
|
|
else
|
|
echo 'Do you want to define operating system'"'"'s IP address?'
|
|
select arg_os_ip in 'Yes (let me specify)' 'No (DHCP is fine)'; do
|
|
case "${arg_os_ip}" in
|
|
'Yes (let me specify)')
|
|
want_own_ip_in_os='true'
|
|
want_dns_and_ntp='true'
|
|
break
|
|
;;
|
|
'No (DHCP is fine)')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_own_ip_in_os}" ]]; then
|
|
read -u3 -p 'Interface IP address with CIDR prefix (a.b.c.d/nn): ' ARCHZBM_OS_CLIENT_IP
|
|
echo
|
|
|
|
read -u3 -p 'Gateway IP address: ' ARCHZBM_OS_GATEWAY_IP
|
|
echo
|
|
fi
|
|
fi
|
|
|
|
if [[ "${want_ssh_in_zbm}" ]]; then
|
|
echo 'Do you want to define OS '"'"'root'"'"' user'"'"'s SSH pub key?'
|
|
select arg_root_pub_keys in 'Yes (let me specify)' 'Yes (use ZBM pub keys)' 'No (don'"'"'t enable sshd.service)'; do
|
|
case "${arg_root_pub_keys}" in
|
|
'Yes (let me specify)')
|
|
want_custom_pub_keys_in_os='true'
|
|
break
|
|
;;
|
|
'Yes (use ZBM pub keys)')
|
|
ARCHZBM_OS_SSH_AUTH_KEYS="${ARCHZBM_SSH_AUTH_KEYS}"
|
|
break
|
|
;;
|
|
'No (don'"'"'t enable sshd.service)')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_custom_pub_keys_in_os}" ]]; then
|
|
read -u3 -p 'Please type SSH pub keys on one line separated by double-commas (,,) and confirm with <Enter>: ' ARCHZBM_OS_SSH_AUTH_KEYS
|
|
echo
|
|
fi
|
|
else
|
|
echo 'Do you want to define OS root user'"'"'s SSH pub key?'
|
|
select arg_root_pub_keys in 'Yes (let me specify)' 'No (don'"'"'t enable sshd.service)'; do
|
|
case "${arg_root_pub_keys}" in
|
|
'Yes (let me specify)')
|
|
want_own_pub_key_in_os='true'
|
|
break
|
|
;;
|
|
'No (don'"'"'t enable sshd.service)')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_own_pub_key_in_os}" ]]; then
|
|
read -u3 -p 'Please type SSH pub keys on one line separated by double-commas (,,) and confirm with <Enter>: ' ARCHZBM_OS_SSH_AUTH_KEYS
|
|
echo
|
|
fi
|
|
fi
|
|
|
|
if [[ "${want_dns_and_ntp}" ]]; then
|
|
read -u3 -p 'Specify one or more comma-separated DNS IPs: ' ARCHZBM_OS_DNS_IP
|
|
echo
|
|
|
|
echo 'Do you want to override Arch Linux'"'"' NTP servers?'
|
|
select arg_custom_ntp in 'Yes' 'No'; do
|
|
case "${arg_custom_ntp}" in
|
|
'Yes')
|
|
want_own_ntp='true'
|
|
break
|
|
;;
|
|
'No')
|
|
break
|
|
;;
|
|
esac
|
|
done <&3 && echo
|
|
|
|
if [[ "${want_own_ntp}" ]]; then
|
|
read -u3 -p 'Specify one or more comma-separated NTP hostnames or IPs: ' ARCHZBM_OS_NTP_IP
|
|
echo
|
|
fi
|
|
fi
|
|
|
|
for env_var in 'ARCHZBM_KERNEL_VER' 'ARCHZBM_ZFSPROPS_NO_COMPRESSION' 'ARCHZBM_ZFSPROPS_NO_ENCRYPTION' 'ARCHZBM_ZPOOL_PASSWORD' 'ARCHZBM_ROOT_PASSWORD' 'ARCHZBM_NET_AUTOCONF' 'ARCHZBM_NET_DEVICE' 'ARCHZBM_NET_CLIENT_IP' 'ARCHZBM_NET_NETMASK' 'ARCHZBM_NET_GATEWAY_IP' 'ARCHZBM_SSH_PORT' 'ARCHZBM_SSH_KEEPALIVE_INTVL' 'ARCHZBM_SSH_AUTH_KEYS' 'ARCHZBM_OS_CLIENT_IP' 'ARCHZBM_OS_GATEWAY_IP' 'ARCHZBM_OS_SSH_AUTH_KEYS' 'ARCHZBM_OS_DNS_IP' 'ARCHZBM_OS_NTP_IP'; do
|
|
if [[ "${!env_var}" ]]; then
|
|
printf -- '%s='"'"'%s'"'"'\n' \
|
|
"${env_var}" "${!env_var}" \
|
|
>> "${settings_file}"
|
|
fi
|
|
done
|
|
|
|
printf -- '%s\n' \
|
|
'Done, please rerun script now with just' \
|
|
'... | bash' \
|
|
'so without the '"'"'setup'"'"' argument'
|
|
exit 77
|
|
}
|
|
|
|
function arg_parse () {
|
|
[[ "${1}" ]] && while :; do
|
|
case "${1}" in
|
|
-[[:alnum:]]*)
|
|
>&3 printf -- '%s\n' \
|
|
'Short options '"'${1}'"' detected. Only known option is' \
|
|
'literal string '"'"'setup'"'"'. No idea what '"'${1}'"' is.' \
|
|
'Exiting ...'
|
|
exit 77
|
|
;;
|
|
--*)
|
|
>&3 printf -- '%s\n' \
|
|
'Long-form option '"'${1}'"' detected. Only known option is' \
|
|
'literal string '"'"'setup'"'"'. No idea what '"'${1}'"' is.' \
|
|
'Exiting ...'
|
|
exit 77
|
|
;;
|
|
setup)
|
|
setup_env_vars
|
|
return
|
|
;;
|
|
*)
|
|
>&3 printf -- '%s\n' \
|
|
'Argument '"'${1}'"' detected. Only known option is literal' \
|
|
'string '"'"'setup'"'"'. No idea what '"'${1}'"' is.' \
|
|
'Exiting ...'
|
|
exit 77
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
function we_are_changerooted () {
|
|
if [ "$(stat -c '%d:%i' '/')" != "$(stat -c '%d:%i' '/proc/1/root/.')" ]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
function no_kernel_update_in_iso () {
|
|
#1.1
|
|
sed -ri -e 's'$'\x1''#(IgnorePkg)[^\r\n\f]+'$'\x1''\1 = linux linux-headers'$'\x1''g' /etc/pacman.conf
|
|
}
|
|
|
|
function set_ntp () {
|
|
#1.2
|
|
timedatectl set-ntp true
|
|
}
|
|
|
|
function resize_cow_space () {
|
|
#1.3
|
|
mount -o remount,size='50%' /run/archiso/cowspace
|
|
}
|
|
|
|
function update_pacman_db () {
|
|
#1.4
|
|
pacman_dl_parallel
|
|
printf -- '%s\n' 'Refreshing mirror list ...'
|
|
printf -- '%s\n' \
|
|
'--save /etc/pacman.d/mirrorlist' \
|
|
'--protocol https' \
|
|
'--latest 5' \
|
|
'--sort age' \
|
|
> '/etc/xdg/reflector/reflector.conf'
|
|
systemctl start reflector
|
|
# In an ISO and for the minimal number of packages we need we do not
|
|
# care about partial upgrades
|
|
#
|
|
# Are we better off not attempting an upgrade inside the ISO?
|
|
# Let's try and find out.
|
|
# while ! pacman -Syyuu --needed --noconfirm --downloadonly; do
|
|
# sleep 5
|
|
# done
|
|
# pacman -Syyuu --needed --noconfirm
|
|
pacman -Syy
|
|
}
|
|
|
|
function install_pkgs () {
|
|
#1.5
|
|
printf -- '%s\n' 'Installing packages ...'
|
|
while ! pacman -S --needed --noconfirm --downloadonly "${@}"; do
|
|
sleep 5
|
|
done
|
|
pacman -S --needed --noconfirm "${@}"
|
|
}
|
|
|
|
function install_zfs () {
|
|
#1.6
|
|
declare reset_colors='\033[0m'
|
|
if modinfo 'zfs' &>/dev/null; then
|
|
>&3 printf -- '%s\n' \
|
|
'ZFS kernel module is loaded, no need to install ...'
|
|
else
|
|
curl -s 'https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init' | bash
|
|
fi
|
|
printf -- "${reset_colors}"
|
|
}
|
|
|
|
function uefi_or_bios () {
|
|
#1.7
|
|
local part_count_linux part_count_efi
|
|
# Select disks with at least one partition. Among them count how many
|
|
# with the given partition type code we have.
|
|
part_count_linux="$(get_partitions | jq --raw-output '[.[][] | select(.children | length > 0) | .children[] | select(.parttype=="'"${partition_types[mbr_boot]}"'") | .path] | length')"
|
|
part_count_efi="$(get_partitions | jq --raw-output '[.[][] | select(.children | length > 0) | .children[] | select(.parttype=="'"${partition_types[gpt_efi]}"'") | .path] | length')"
|
|
if [[ "${part_count_linux}" -eq '1' ]] && [[ "${part_count_efi}" -eq '0' ]]; then
|
|
part_schema='mbr'
|
|
>&3 printf -- '%s\n' \
|
|
'Treating this as an MBR/legacy BIOS machine ...'
|
|
elif [[ "${part_count_linux}" -eq '0' ]] && [[ "${part_count_efi}" -eq '1' ]]; then
|
|
part_schema='gpt'
|
|
>&3 printf -- '%s\n' \
|
|
'Treating this as a GPT/UEFI machine ...'
|
|
else
|
|
>&3 printf -- '%s\n' \
|
|
'We are seeing partitions as follows:' \
|
|
'- Partition type code '"${partition_types[mbr_boot]}"': '"${part_count_linux}" \
|
|
'- Partition type code '"${partition_types[gpt_efi]}"': '"${part_count_efi}" \
|
|
'' \
|
|
'We are instead expecting either 1 and 0 indicating a legacy' \
|
|
'BIOS setup or 0 and 1 indicating UEFI. '"${part_count_linux}"' and '"${part_count_efi}"' is' \
|
|
'neither. Cowardly exiting ...'
|
|
exit 77
|
|
fi
|
|
export part_schema="${part_schema}"
|
|
}
|
|
|
|
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 part_type_code
|
|
declare parttype parts
|
|
parttype="${1:?}"
|
|
zfs_install_drive="${2:-}"
|
|
case "${parttype}" in
|
|
zfs)
|
|
if [[ "${part_schema}" = 'mbr' ]]; then
|
|
part_type_code="${partition_types[mbr_zfs]}"
|
|
else
|
|
part_type_code="${partition_types[gpt_zfs]}"
|
|
fi
|
|
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | .children[] | select(.parttype=="'"${part_type_code}"'") | .path')" || exit 1
|
|
;;
|
|
boot)
|
|
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.path=="'"${zfs_install_drive:?}"'") | .children[] | select(.parttype=="'"${partition_types[mbr_boot]}"'") | .path')" || exit 1
|
|
;;
|
|
efi)
|
|
parts="$(get_partitions | jq --raw-output '.[][] | select(.children | length > 0) | select(.path=="'"${zfs_install_drive:?}"'") | .children[] | select(.parttype=="'"${partition_types[gpt_efi]}"'") | .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 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
|
|
>&3 printf -- '%s\n' 'No '"'${1:?}'"' partition entry in /dev/disk/by-partuuid, exiting ...'
|
|
exit 77
|
|
}
|
|
|
|
function select_part () {
|
|
local parts enriched_parts enriched_parts_count part_number part_type zfs_install_drive part_type_code specific_part_type
|
|
declare part
|
|
part_type="${1:?}" # 'boot' or 'zfs'
|
|
zfs_install_drive="${2:-}"
|
|
|
|
if [[ "${part_type}" = 'boot' ]]; then
|
|
if [[ "${part_schema}" = 'mbr' ]]; then
|
|
specific_part_type='boot'
|
|
else
|
|
specific_part_type='efi'
|
|
fi
|
|
else
|
|
specific_part_type="${part_type}"
|
|
fi
|
|
|
|
if [[ "${zfs_install_drive}" ]]; then
|
|
# This is intended to find correct boot/EFI partition
|
|
parts="$(get_parts "${specific_part_type}" "${zfs_install_drive}")"
|
|
else
|
|
parts="$(get_parts "${specific_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)
|
|
if [[ "${part_schema}" = 'mbr' ]]; then
|
|
part_type_code='bf'
|
|
else
|
|
part_type_code='BF00'
|
|
fi
|
|
part_type_human_readable='ZFS zpool partition with partition type code '"${part_type_code}"
|
|
;;
|
|
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 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 () {
|
|
local zpool_password
|
|
if [[ "${ARCHZBM_ZPOOL_PASSWORD}" ]]; then
|
|
zpool_password="${ARCHZBM_ZPOOL_PASSWORD}"
|
|
else
|
|
zpool_password='password'
|
|
fi
|
|
# May or may not have a newline at the end, ZFS doesn't care
|
|
printf -- '%s' "${zpool_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' -o 'canmount=off' "${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 load_settings_file () {
|
|
#1.8
|
|
local working_dir settings_abs
|
|
working_dir="$(pwd)"
|
|
settings_abs="${working_dir}"'/'"${settings_file}"
|
|
if [[ -r "${settings_abs}" ]]; then
|
|
set -a
|
|
source "${settings_abs}"
|
|
set +a
|
|
fi
|
|
}
|
|
|
|
function setup_zpool () {
|
|
#1.9
|
|
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
|
|
>&3 printf -- '%s\n' 'A zpool already exists, that is unexpected. Cowardly exiting 1 ...'
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function mount_system () {
|
|
#1.10
|
|
zfs mount "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
|
|
zfs mount -a
|
|
|
|
local zfs_parent efi_part boot_part
|
|
zfs_parent="$(get_part_parent "${zpool_drive:?}")"
|
|
|
|
if [[ "${part_schema}" = 'mbr' ]]; then
|
|
boot_drive="${zfs_parent}"
|
|
export boot_drive="${boot_drive}"
|
|
boot_part="$(select_part 'boot' "${zfs_parent:?}")"
|
|
mkdir -p '/mnt/boot/syslinux'
|
|
mount "${boot_part}" '/mnt/boot/syslinux'
|
|
else
|
|
efi_drive="${zfs_parent}"
|
|
export efi_drive="${efi_drive}"
|
|
efi_part="$(select_part 'boot' "${zfs_parent:?}")"
|
|
mkdir -p '/mnt/efi'
|
|
mount "${efi_part}" '/mnt/efi'
|
|
fi
|
|
}
|
|
|
|
function copy_zpool_cache () {
|
|
#1.11
|
|
mkdir -p '/mnt/etc/zfs'
|
|
zpool set 'cachefile=/etc/zfs/'"${zpool_name}"'.cache' "${zpool_name}"
|
|
}
|
|
|
|
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.12
|
|
pacman_dl_parallel
|
|
pacman_dont_check_space
|
|
while ! 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; do
|
|
sleep 5
|
|
done
|
|
}
|
|
|
|
function gen_fstab () {
|
|
#1.13
|
|
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.14
|
|
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.15
|
|
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 in_file_in_array_insert_n_before_m () {
|
|
local arg_file arg_array arg_string arg_precede
|
|
arg_file="${1:?}"
|
|
arg_array="${2:?}"
|
|
arg_string="${3:?}"
|
|
arg_precede="${4:?}"
|
|
|
|
# Look for a line that contains in this order
|
|
# - String "${arg_array}" and a equals sign (=), assign capture group \1
|
|
# - Followed by as few characters as possible followed by either an
|
|
# opening parenthesis or a space character, assign capture group \2
|
|
# - String "${arg_precede}", capture as capture group \3
|
|
# - Followed by either a closing parenthesis or a space which are then
|
|
# followed by as many non-line break characters as possible, capture
|
|
# as capture group \2
|
|
#
|
|
# For following example text we're assuming that:
|
|
# - "${arg_array}" equals 'HOOKS'
|
|
# - "${arg_precede}" equals 'filesystems'
|
|
# - "${arg_string}" equals 'zfs'
|
|
#
|
|
# This finds a 'HOOKS=' array definition that contains the string
|
|
# 'filesystems' either at the beginning of the 'HOOKS=(...)' opening
|
|
# parenthesis, at the very end or somewhere in the middle where it may
|
|
# be preceded or followed by one or more space characters. It saves
|
|
# 'HOOKS=', it saves whatever precedes 'filesystems' and 'filesystems'
|
|
# itself plus whatever comes after 'filesystems' until end of line. It
|
|
# lastly inserts 'zfs' and a space character right in front of
|
|
# 'filesystems'.
|
|
sed -ri \
|
|
-e 's'$'\x1''('"${arg_array}"'=)(.*?[( ])('"${arg_precede}"')([) ][^\r\n\f]*)'$'\x1''\1\2'"${arg_string}"' \3\4'$'\x1''g' \
|
|
"${arg_file}"
|
|
}
|
|
|
|
function in_file_in_array_insert_n_at_the_end () {
|
|
local arg_file arg_array arg_string
|
|
arg_file="${1:?}"
|
|
arg_array="${2:?}"
|
|
arg_string="${3:?}"
|
|
|
|
# Look for end of array, insert "${arg_string}" right before
|
|
#
|
|
# For following example text we're assuming that:
|
|
# - "${arg_array}" equals 'HOOKS'
|
|
# - "${arg_string}" equals 'net'
|
|
#
|
|
# This:
|
|
# - Finds a 'HOOKS=' array definition, saves it as \1
|
|
# - Finds as many non-closing parenthesis characters as possible, so all
|
|
# characters until just before the final ')' character of the line and
|
|
# saves this as \2
|
|
# - Finds the closing parenthesis character plus all non-line break
|
|
# characters until end of line that come after it and saves this as \3
|
|
# - Adds a space character and 'net' after \2
|
|
sed -ri \
|
|
-e 's'$'\x1''('"${arg_array}"'=)([^)]*)(\)[^\r\n\f]*)'$'\x1''\1\2 '"${arg_string}"'\3'$'\x1''g' \
|
|
"${arg_file}"
|
|
}
|
|
|
|
function in_file_in_array_remove_n () {
|
|
local arg_file arg_array arg_string
|
|
arg_file="${1:?}"
|
|
arg_array="${2:?}"
|
|
arg_string="${3:?}"
|
|
|
|
# Look for any line that contains "${arg_string}", delete that string
|
|
#
|
|
# For following example text we're assuming that:
|
|
# - "${arg_array}" equals 'HOOKS'
|
|
# - "${arg_string}" equals 'fsck'
|
|
#
|
|
# First -e expression removes 'fsck' wherever it is defined as the one
|
|
# and only element of any HOOKS=(fsck) should such a line exist.
|
|
#
|
|
# Second -e expression finds string 'fsck' where it's preceded by space
|
|
# character(s) and followed by either space character(s) or a closing
|
|
# parenthesis.
|
|
#
|
|
# Third -e expression finds string 'fsck' where it's preceded by space
|
|
# character(s) or an opening parenthesis and followed space
|
|
# character(s).
|
|
sed -ri \
|
|
-e 's'$'\x1''((\()('"${arg_string}"')(\)))'$'\x1''\2\4'$'\x1''g' \
|
|
-e 's'$'\x1''('"${arg_array}"'=.*?)([[:space:]]+'"${arg_string}"')([[:space:]]+|\))'$'\x1''\1\3'$'\x1''g' \
|
|
-e 's'$'\x1''('"${arg_array}"'=.*?)([[:space:]]+|\()('"${arg_string}"'[[:space:]]+)'$'\x1''\1\2'$'\x1''g' \
|
|
"${arg_file}"
|
|
}
|
|
|
|
function add_zfs_hook_to_initramfs () {
|
|
#1.16
|
|
# Add zfs hook, remove fsck hook from initramfs.
|
|
in_file_in_array_insert_n_before_m '/mnt/etc/mkinitcpio.conf' 'HOOKS' 'zfs' 'filesystems'
|
|
in_file_in_array_remove_n '/mnt/etc/mkinitcpio.conf' 'HOOKS' 'fsck'
|
|
# 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.17
|
|
# 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.18
|
|
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 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 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
|
|
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 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'
|
|
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-bin.git
|
|
chown -R 'build:' 'paru-bin'
|
|
pushd 'paru-bin'
|
|
sudo --user 'build' makepkg -si --noconfirm
|
|
popd
|
|
rm -rf 'paru-bin'
|
|
popd
|
|
}
|
|
|
|
function paru_install () {
|
|
sudo --user build paru -S --noconfirm "${@}"
|
|
}
|
|
|
|
function configure_syslinux () {
|
|
paru_install 'syslinux'
|
|
cp /usr/lib/syslinux/bios/*.c32 /boot/syslinux
|
|
extlinux --install /boot/syslinux
|
|
cat > /boot/syslinux/syslinux.cfg <<EOF
|
|
UI menu.c32
|
|
PROMPT 0
|
|
|
|
MENU TITLE ZFSBootMenu
|
|
TIMEOUT 100
|
|
|
|
DEFAULT zfsbootmenu
|
|
|
|
LABEL zfsbootmenu
|
|
MENU LABEL ZFSBootMenu
|
|
KERNEL /zfsbootmenu/vmlinuz-bootmenu
|
|
INITRD /zfsbootmenu/initramfs-bootmenu.img
|
|
APPEND zfsbootmenu quiet loglevel=0
|
|
|
|
LABEL zfsbootmenu-backup
|
|
MENU LABEL ZFSBootMenu (Backup)
|
|
KERNEL /zfsbootmenu/vmlinuz-bootmenu-backup
|
|
INITRD /zfsbootmenu/initramfs-bootmenu-backup.img
|
|
APPEND zfsbootmenu quiet loglevel=0
|
|
EOF
|
|
dd bs=440 count=1 conv=notrunc if='/usr/lib/syslinux/bios/mbr.bin' of="${boot_drive}"
|
|
}
|
|
|
|
function configure_zfsbootmenu () {
|
|
#2.9
|
|
paru_install 'zfsbootmenu'
|
|
in_file_in_array_remove_n '/etc/zfsbootmenu/mkinitcpio.conf' 'HOOKS' 'zfsbootmenu'
|
|
|
|
if [[ "${part_schema}" = 'gpt' ]]; then
|
|
cat > '/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
|
|
else
|
|
configure_syslinux
|
|
cat > '/etc/zfsbootmenu/config.yaml' <<EOF
|
|
Global:
|
|
ManageImages: true
|
|
BootMountPoint: /boot/syslinux
|
|
InitCPIO: true
|
|
Components:
|
|
Enabled: true
|
|
Versions: false
|
|
ImageDir: /boot/syslinux/zfsbootmenu
|
|
Kernel:
|
|
Prefix: vmlinuz
|
|
EOF
|
|
fi
|
|
|
|
# Up here maybe 'ro quiet' instead of 'ro'. This is ZFSBootMenu's kernel
|
|
# command line. In MBR/Syslinux/extlinux mode /it/ must pass arguments to
|
|
# ZFSBootMenu's kernel so check /boot/syslinux/syslinux.cfg for how we start
|
|
# ZFSBootMenu in this mode. The .Kernel.CommandLine in
|
|
# /etc/zfsbootmenu/config.yaml is irrelevant for us in MBR/Syslinux/extlinux
|
|
# mode.
|
|
|
|
# Assign cmdline for final kernel start. This is our Arch Linx kernel
|
|
# command line.
|
|
zfs set org.zfsbootmenu:commandline='rw nowatchdog rd.vconsole.keymap=de-latin1' "${zpool_name}"'/root/'"${zfs_arch_dataset_name}"
|
|
}
|
|
|
|
function get_dropbear_hooks () {
|
|
mkdir -p '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main'
|
|
git -C '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main' clone 'https://quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook.git' .
|
|
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-hook.sh' '/usr/local/bin/pacman-mkinitcpio-dropbear-hook'
|
|
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-install.sh' '/usr/local/bin/pacman-mkinitcpio-dropbear-install'
|
|
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-hook.hook' '/usr/share/libalpm/hooks/pacman-mkinitcpio-dropbear-hook.hook'
|
|
ln -s '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/pacman-mkinitcpio-dropbear-install.hook' '/usr/share/libalpm/hooks/pacman-mkinitcpio-dropbear-install.hook'
|
|
}
|
|
|
|
function customize_dropbear_hooks () {
|
|
local env_archzbm_ssh_port env_archzbm_ssh_keepalive_intvl
|
|
env_archzbm_ssh_port="${ARCHZBM_SSH_PORT:-22}"
|
|
env_archzbm_ssh_keepalive_intvl="${ARCHZBM_SSH_KEEPALIVE_INTVL:-1}"
|
|
if [[ "${env_archzbm_ssh_port}" -ne '22' ]] || [[ "${env_archzbm_ssh_keepalive_intvl}" -ne '1' ]]; then
|
|
paru_install 'rsync'
|
|
rsync -av '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/dropbear_hook'{,'.override'}'.patch'
|
|
fi
|
|
if [[ "${env_archzbm_ssh_port}" -ne '22' ]]; then
|
|
sed -ri -e 's'$'\x1''-p [[:digit:]]+'$'\x1''-p '"${env_archzbm_ssh_port}"''$'\x1''g' '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/dropbear_hook.override.patch'
|
|
fi
|
|
if [[ "${env_archzbm_ssh_keepalive_intvl}" -ne '1' ]]; then
|
|
sed -ri -e 's'$'\x1''-K [[:digit:]]+'$'\x1''-K '"${env_archzbm_ssh_keepalive_intvl}"''$'\x1''g' '/opt/git/quico.space/quico-os-setup/mkinitcpio-dropbear-pacman-hook/branches/main/dropbear_hook.override.patch'
|
|
fi
|
|
}
|
|
|
|
function set_unique_ip_in_syslinux_kcl () {
|
|
local zbm_config default_ip
|
|
zbm_config="${1:?}"
|
|
default_ip="${2:?}"
|
|
|
|
# First -e expression removes first looks for lines that contain
|
|
# 'APPEND' plus a space character and only in those lines removes all
|
|
# occurrences of ' ip=' followed by as many non-space characters as
|
|
# possible. This removes whatever 'ip=' definition already was present.
|
|
#
|
|
# Second -e expression similarly looks for lines that contain 'APPEND'
|
|
# plus a space character then at their end inserts a space plus our
|
|
# desired new 'ip=' definition. This puts in place the 'ip=' we want.
|
|
sed -ri \
|
|
-e \\$'\x1''APPEND '$'\x1 s'$'\x1'' ip=([^[:space:]]*)'$'\x1'''$'\x1''gi' \
|
|
-e \\$'\x1''APPEND '$'\x1 s'$'\x1''$'$'\x1'' '"${default_ip}"''$'\x1''gi' \
|
|
"${zbm_config}"
|
|
}
|
|
|
|
function ensure_ip_in_kcl () {
|
|
local default_ip
|
|
default_ip='ip='"${ARCHZBM_NET_CLIENT_IP}"':'"${ARCHZBM_NET_SERVER_IP}"':'"${ARCHZBM_NET_GATEWAY_IP}"':'"${ARCHZBM_NET_NETMASK}"':'"${ARCHZBM_NET_HOSTNAME}"':'"${ARCHZBM_NET_DEVICE}"':'"${ARCHZBM_NET_AUTOCONF}"
|
|
|
|
if [[ "${part_schema}" = 'gpt' ]]; then
|
|
local zbm_config kcl_length kcl_string ip_addr_found new_kcl first_kcl_elem
|
|
local -a kcl
|
|
paru_install 'go-yq'
|
|
|
|
zbm_config='/etc/zfsbootmenu/config.yaml'
|
|
kcl_length="$(yq '.Kernel.CommandLine | length' "${zbm_config}")"
|
|
if [[ "${kcl_length}" -eq '0' ]]; then
|
|
>&3 printf -- '%s\n' \
|
|
'No .Kernel.CommandLine YAML element with content found in '"${zbm_config}"'. Exiting ...'
|
|
exit 77
|
|
else
|
|
kcl_string="$(yq '.Kernel.CommandLine' "${zbm_config}")"
|
|
fi
|
|
|
|
mapfile -t kcl < <(<<<"${kcl_string}" tr ' ' '\n' | sed '/^$/d')
|
|
for kcl_elem in "${!kcl[@]}"; do
|
|
if grep -Piq -- 'ip=' <<<"${kcl[$kcl_elem]}"; then
|
|
ip_addr_found='true'
|
|
kcl["${kcl_elem}"]="${default_ip}"
|
|
fi
|
|
done
|
|
if [[ ! "${ip_addr_found}" ]]; then
|
|
kcl+=("${default_ip}")
|
|
fi
|
|
new_kcl=''
|
|
first_kcl_elem='true'
|
|
for kcl_elem in "${kcl[@]}"; do
|
|
if [[ ! "${first_kcl_elem}" ]]; then
|
|
new_kcl+=' '"${kcl_elem}"
|
|
else
|
|
new_kcl+="${kcl_elem}"
|
|
unset -v first_kcl_elem
|
|
fi
|
|
done
|
|
yq -i '.Kernel.CommandLine = "'"${new_kcl}"'"' "${zbm_config}"
|
|
else
|
|
local zbm_config
|
|
zbm_config='/boot/syslinux/syslinux.cfg'
|
|
set_unique_ip_in_syslinux_kcl "${zbm_config}" "${default_ip}"
|
|
fi
|
|
}
|
|
|
|
function set_pub_keys () {
|
|
local authorized_keys_file raw_pub_keys
|
|
authorized_keys_file="${1:?}"
|
|
raw_pub_keys="${2:?}"
|
|
:> "${authorized_keys_file}"
|
|
while IFS= read -r pub_key_line; do
|
|
printf -- '%s\n' "${pub_key_line}" >> "${authorized_keys_file}"
|
|
done < <(<<<"${raw_pub_keys}" sed -r -e 's/,,/\n/g')
|
|
sed -i '/^$/d' "${authorized_keys_file}"
|
|
}
|
|
|
|
function we_want_ssh () {
|
|
#2.10
|
|
if [[ "${ARCHZBM_NET_CLIENT_IP}" ]] || \
|
|
[[ "${ARCHZBM_NET_SERVER_IP}" ]] || \
|
|
[[ "${ARCHZBM_NET_GATEWAY_IP}" ]] || \
|
|
[[ "${ARCHZBM_NET_NETMASK}" ]] || \
|
|
[[ "${ARCHZBM_NET_HOSTNAME}" ]] || \
|
|
[[ "${ARCHZBM_NET_DEVICE}" ]] || \
|
|
[[ "${ARCHZBM_NET_AUTOCONF}" ]] || \
|
|
[[ "${ARCHZBM_SSH_PORT}" ]] || \
|
|
[[ "${ARCHZBM_SSH_KEEPALIVE_INTVL}" ]] || \
|
|
[[ "${ARCHZBM_SSH_AUTH_KEYS}" ]]; then
|
|
>&3 printf -- '%s\n' 'Installing SSH in ZFSBootMenu'
|
|
return 0
|
|
fi
|
|
>&3 printf -- '%s\n' 'Not installing SSH in ZFSBootMenu'
|
|
return 1
|
|
}
|
|
|
|
function configure_ssh_in_zbm () {
|
|
#2.11
|
|
get_dropbear_hooks
|
|
customize_dropbear_hooks
|
|
paru_install 'mkinitcpio-nfs-utils' 'dropbear' 'mkinitcpio-dropbear'
|
|
in_file_in_array_insert_n_at_the_end '/etc/zfsbootmenu/mkinitcpio.conf' 'HOOKS' 'net'
|
|
in_file_in_array_insert_n_at_the_end '/etc/zfsbootmenu/mkinitcpio.conf' 'HOOKS' 'dropbear'
|
|
for key_type in 'dss' 'ecdsa' 'ed25519' 'rsa'; do
|
|
dropbearkey -t "${key_type}" -f '/etc/dropbear/dropbear_'"${key_type}"'_host_key'
|
|
done
|
|
set_pub_keys '/etc/dropbear/root_key' "${ARCHZBM_SSH_AUTH_KEYS}"
|
|
ensure_ip_in_kcl
|
|
}
|
|
|
|
function add_syslinux_pacman_hook () {
|
|
mkdir -p '/opt/git/quico.space/quico-os-setup/zbm-syslinux-pacman-hook/branches/main'
|
|
git -C '/opt/git/quico.space/quico-os-setup/zbm-syslinux-pacman-hook/branches/main' clone 'https://quico.space/quico-os-setup/zbm-syslinux-pacman-hook.git' .
|
|
chmod +x '/opt/git/quico.space/quico-os-setup/zbm-syslinux-pacman-hook/branches/main/pacman-zbm-syslinux-regen.sh'
|
|
ln -s '/opt/git/quico.space/quico-os-setup/zbm-syslinux-pacman-hook/branches/main/pacman-zbm-syslinux-regen.sh' '/usr/local/bin/pacman-zbm-syslinux-regen'
|
|
ln -s '/opt/git/quico.space/quico-os-setup/zbm-syslinux-pacman-hook/branches/main/pacman-zbm-syslinux-regen.hook' '/usr/share/libalpm/hooks/pacman-zbm-syslinux-regen.hook'
|
|
}
|
|
|
|
function add_zbm_pacman_hook () {
|
|
mkdir -p '/opt/git/quico.space/quico-os-setup/zbm-regen-pacman-hook/branches/main'
|
|
git -C '/opt/git/quico.space/quico-os-setup/zbm-regen-pacman-hook/branches/main' clone 'https://quico.space/quico-os-setup/zbm-regen-pacman-hook.git' .
|
|
ln -s '/opt/git/quico.space/quico-os-setup/zbm-regen-pacman-hook/branches/main/pacman-zbm-image-regen.hook' '/usr/share/libalpm/hooks/pacman-zbm-image-regen.hook'
|
|
}
|
|
|
|
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=="'"${partition_types[gpt_efi]}"'")) and ([select(.children[].parttype=="'"${partition_types[gpt_efi]}"'")] | 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 () {
|
|
#2.2
|
|
dd if='/dev/zero' of='/swapfile' bs='1M' count='2048'
|
|
losetup '/dev/loop9' '/swapfile'
|
|
mkswap '/dev/loop9'
|
|
swapon '/dev/loop9'
|
|
### 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
|
|
while ! pacman -S archlinux-keyring --noconfirm --downloadonly; do
|
|
sleep 5
|
|
done
|
|
pacman -S archlinux-keyring --noconfirm
|
|
|
|
locale-gen
|
|
source /etc/locale.conf
|
|
|
|
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
|
|
if [[ "${ARCHZBM_KERNEL_VER}" ]]; then
|
|
paru_install 'downgrade'
|
|
yes | downgrade --ala-only \
|
|
'linux='"${ARCHZBM_KERNEL_VER}" \
|
|
'linux-headers='"${ARCHZBM_KERNEL_VER}" \
|
|
--ignore always
|
|
fi
|
|
paru_install 'zfs-dkms' 'zfs-utils' 'jq'
|
|
hwclock --systohc
|
|
mkinitcpio -P
|
|
|
|
# Install ZFSBootMenu image
|
|
configure_zfsbootmenu #2.9
|
|
if we_want_ssh; then #2.10
|
|
configure_ssh_in_zbm #2.11
|
|
fi
|
|
generate-zbm
|
|
|
|
# Yes, we do this twice so we immediately get a functional backup file
|
|
generate-zbm
|
|
|
|
if [[ "${part_schema}" = 'mbr' ]]; then
|
|
paru_install 'rsync'
|
|
add_syslinux_pacman_hook
|
|
fi
|
|
add_zbm_pacman_hook
|
|
swapoff '/dev/loop9'
|
|
losetup -d '/dev/loop9'
|
|
rm '/swapfile'
|
|
}
|
|
|
|
function set_root_pw () {
|
|
#3.2
|
|
local root_password
|
|
if [[ "${ARCHZBM_ROOT_PASSWORD}" ]]; then
|
|
root_password="${ARCHZBM_ROOT_PASSWORD}"
|
|
else
|
|
root_password='password'
|
|
fi
|
|
printf -- '%s\n' 'root:'"${root_password}" | chpasswd --crypt-method 'SHA512' --root '/mnt'
|
|
}
|
|
|
|
function configure_networking () {
|
|
#3.3
|
|
local -a dns_addresses ntp_addresses
|
|
|
|
# Begin network unit file with a default top section
|
|
cat > '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
|
|
[Match]
|
|
Name=en*
|
|
|
|
[Network]
|
|
EOF
|
|
|
|
# Decide on what comes next in network unit file
|
|
if [[ "${ARCHZBM_OS_CLIENT_IP}" ]] || \
|
|
[[ "${ARCHZBM_OS_GATEWAY_IP}" ]] || \
|
|
[[ "${ARCHZBM_OS_DNS_IP}" ]] || \
|
|
[[ "${ARCHZBM_OS_NTP_IP}" ]]; then
|
|
|
|
cat >> '/mnt/etc/systemd/network/50-wired.network' <<EOF
|
|
Address=${ARCHZBM_OS_CLIENT_IP}
|
|
Gateway=${ARCHZBM_OS_GATEWAY_IP}
|
|
EOF
|
|
|
|
if [[ "${ARCHZBM_OS_DNS_IP}" ]]; then
|
|
mapfile -t dns_addresses < <(<<<"${ARCHZBM_OS_DNS_IP}" tr ',' '\n' | sed '/^$/d')
|
|
else
|
|
dns_addresses+=('8.8.8.8')
|
|
dns_addresses+=('8.8.4.4')
|
|
fi
|
|
for dns_addr in "${dns_addresses[@]}"; do
|
|
cat >> '/mnt/etc/systemd/network/50-wired.network' <<EOF
|
|
DNS=${dns_addr}
|
|
EOF
|
|
done
|
|
|
|
if [[ "${ARCHZBM_OS_NTP_IP}" ]]; then
|
|
mapfile -t ntp_addresses < <(<<<"${ARCHZBM_OS_NTP_IP}" tr ',' '\n' | sed '/^$/d')
|
|
for ntp_addr in "${ntp_addresses[@]}"; do
|
|
cat >> '/mnt/etc/systemd/network/50-wired.network' <<EOF
|
|
NTP=${ntp_addr}
|
|
EOF
|
|
done
|
|
fi
|
|
|
|
cat >> '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
|
|
IPForward=yes
|
|
Domains=~.
|
|
EOF
|
|
else
|
|
cat >> '/mnt/etc/systemd/network/50-wired.network' <<"EOF"
|
|
DHCP=ipv4
|
|
IPForward=yes
|
|
|
|
[DHCP]
|
|
UseDNS=yes
|
|
RouteMetric=10
|
|
EOF
|
|
fi
|
|
|
|
systemctl enable 'systemd-networkd' --root='/mnt'
|
|
systemctl disable 'systemd-networkd-wait-online' --root='/mnt'
|
|
}
|
|
|
|
function configure_sshd () {
|
|
#3.4
|
|
local pub_key_line
|
|
|
|
cat >> '/mnt/etc/ssh/sshd_config.d/40-defaults.conf' <<"EOF"
|
|
PasswordAuthentication no
|
|
PermitRootLogin yes
|
|
EOF
|
|
|
|
while IFS= read -r pub_key_line; do
|
|
printf -- '%s\n' "${pub_key_line}" >> '/mnt/root/.ssh/authorized_keys'
|
|
done < <(<<<"${ARCHZBM_OS_SSH_AUTH_KEYS}" sed -r -e 's/,,/\n/g')
|
|
|
|
systemctl enable 'sshd.service' --root='/mnt'
|
|
}
|
|
|
|
function configure_dns () {
|
|
#3.5
|
|
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.service' --root='/mnt'
|
|
}
|
|
|
|
function configure_ntp () {
|
|
#3.6
|
|
systemctl enable 'systemd-timesyncd.service' --root='/mnt'
|
|
}
|
|
|
|
function configure_reflector () {
|
|
#3.7
|
|
systemctl enable 'reflector.service' 'reflector.timer' --root='/mnt'
|
|
}
|
|
|
|
function configure_zfs () {
|
|
#3.8
|
|
systemctl enable 'zfs-import-cache.service' 'zfs-mount.service' 'zfs-import.target' 'zfs.target' --root='/mnt'
|
|
}
|
|
|
|
function configure_zfs_mount_gen () {
|
|
#3.9
|
|
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 set_new_uefi_boot_entries () {
|
|
#3.10
|
|
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}')
|
|
|
|
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%%.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
|
|
}
|
|
|
|
function umount_all () {
|
|
#3.11
|
|
if [[ "${part_schema}" = 'mbr' ]]; then
|
|
umount '/mnt/boot/syslinux'
|
|
else
|
|
umount '/mnt/efi'
|
|
fi
|
|
zfs umount -a
|
|
zpool export "${zpool_name}"
|
|
}
|
|
|
|
function finalize_os_setup () {
|
|
#3.1
|
|
set_root_pw #3.2
|
|
configure_networking #3.3
|
|
if [[ "${ARCHZBM_OS_SSH_AUTH_KEYS}" ]]; then
|
|
configure_sshd #3.4
|
|
fi
|
|
configure_dns #3.5
|
|
configure_ntp #3.6
|
|
configure_reflector #3.7
|
|
configure_zfs #3.8
|
|
configure_zfs_mount_gen #3.9
|
|
if [[ "${part_schema}" = 'gpt' ]]; then
|
|
set_new_uefi_boot_entries #3.10
|
|
fi
|
|
umount_all #3.11
|
|
}
|
|
|
|
function main () {
|
|
if [[ "${#@}" -gt '0' ]]; then
|
|
arg_parse "${@}"
|
|
fi
|
|
if we_are_changerooted; then
|
|
install_os_in_chroot #2.2
|
|
else
|
|
no_kernel_update_in_iso #1.1
|
|
set_ntp #1.2
|
|
resize_cow_space #1.3
|
|
update_pacman_db #1.4
|
|
install_pkgs 'jq' #1.5
|
|
install_zfs #1.6
|
|
uefi_or_bios #1.7
|
|
load_settings_file #1.8
|
|
setup_zpool #1.9
|
|
mount_system #1.10
|
|
copy_zpool_cache #1.11
|
|
install_archlinux #1.12
|
|
gen_fstab #1.13
|
|
set_hostname #1.14
|
|
set_locale #1.15
|
|
add_zfs_hook_to_initramfs #1.16
|
|
set_initramfs_build_list #1.17
|
|
add_zfs_files_to_new_os #1.18
|
|
enter_chroot #2.1
|
|
# We're done in chroot
|
|
finalize_os_setup #3.1
|
|
fi
|
|
}
|
|
|
|
main "${@}"
|