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