#!/bin/bash ######################### # Shared constants export GREEN='\033[0;32m' export RED='\033[0;31m' export WHITE='\033[0;37m' export NORMAL='\033[0m' ######################### ############################################ # Default values for configuration variables export default_imgfile=arch.img export default_architecture=x86_64 export default_disk_size=8G export default_memory=2048 export default_hostname=qemu export default_root_password=root export default_username=user export default_user_password=password export default_timezone=UTC export default_locale=en_US.UTF-8 export default_img_download_page='http://mirror.cj2.nl/archlinux/iso/latest/' export isofile=archlinux-latest.iso ############################################ ################ # install script # Shared variables imgfile= architecture= disk_size= memory= hostname= root_password= username= user_password= timezone= disable_keyring_checks=0 locale="$default_locale" img_download_page="$default_img_download_page" function install_usage() { echo "Install an Arch Linux system on a QEMU disk image" echo echo "Usage: $(basename "$0") install [-o ] [-a ] [-s ]" echo -e "\t[-m ] [-h ] [-P ] [-u ]" echo -e "\t[-p ] [-z ] [-l ] [-M ]" echo -e "\t[-K] [extra qemu arguments]" echo echo -e "-o\t\t\tPath of the output disk image (default: ./arch.img)" echo -e "-a\t\t\t\tTarget architecture (default: x86_64)" echo -e "-s\t\t\t\tDisk size (default: 8G)" echo -e "-m\t\t\t\tRAM size in KB (default: 2048)" echo -e "-h\t\t\t\tVM hostname (default: qemu)" echo -e "-P\t\t\t\tRoot password. If not specified it will be prompted" echo -e "-u\t\t\tUsername for the main non-root user" echo -e "-p\t\tPassword for the non-root user. If not specified it will be prompted" echo -e "-z\t\t\t\tSystem timezone (default: UTC)" echo -e "-l\t\t\t\tSystem locale (default: en_US.UTF-8)" echo -e "-M\t\t\tArch Linux download mirror URL (default: http://mirror.cj2.nl/archlinux/iso/latest/)" echo -e "\t\t\t\t\tConsult https://archlinux.org/download/ for a full list of the available download mirrors." echo -e "-K\t\t\t\t\tDisable pacman keyring checks during installation. It's potentially unsafe," echo -e "\t\t\t\t\tbut it can be an option if downloading the keys takes too long." echo echo "If you want to install an extra list of packages besides the default ones, then" echo "specify them in a file named PKGLIST in the same directory as the disk image file." echo echo "If you want to run a custom post-installation script after the core system has been" echo "installed, then create a custom script named post-install.sh in the same directory as" echo "the disk image file". exit 1 } function download_latest_arch_iso() { latest_iso=$( curl -s "$img_download_page" | grep -e '' | head -1 | sed -r -e 's/^.*.*/\1/' ) if [ ! -f "$latest_iso" ]; then echo -e "${GREEN}Downloading the latest Arch Linux ISO image${NORMAL}" rm -f archlinux*.iso curl -o "${latest_iso}" "${img_download_page}/${latest_iso}" ln -sf "$latest_iso" "$isofile" else echo -e "${GREEN}Latest Arch Linux image already downloaded${NORMAL}" fi } function create_disk_image() { # Create a backing COW image # qemu-img create -o backing_file="$imgfile",backing_fmt=raw -f qcow2 img1.cow if [ ! -f "$imgfile" ]; then echo -e "${GREEN}Creating base disk image${NORMAL}" qemu-img create -f raw "$imgfile" $disk_size else echo -e "${RED}The base disk image file $imgfile already exists.${NORMAL}" echo -e "${RED}Remove it and re-run this script if you intend to run a new fresh installation.${NORMAL}" fi } function _get_ssh_key_name() { keyfiles=$(cat < "$logfile" expect <> /mnt/etc/fstab\r" expect \$prompt # chroot to the newly installed system send -- "arch-chroot /mnt\r" expect \$chroot_prompt # Set the timezone and sync the clock send -- "ln -sf /usr/share/zoneinfo/$timezone /etc/localtime\r" expect \$chroot_prompt send -- "hwclock --systohc\r" expect \$chroot_prompt # Configure localization send -- "echo $locale UTF-8 >> /etc/locale.gen\r" expect \$chroot_prompt send -- "locale-gen\r" expect \$chroot_prompt send -- "echo LANG=en_US.UTF-8 > /etc/locale.conf\r" expect \$chroot_prompt # Set the hostname send -- "echo $hostname > /etc/hostname\r" expect \$chroot_prompt # Generate /etc/hosts send -- "echo -e '127.0.0.1 localhost\\n::1 localhost' >> /etc/hosts\r" expect \$chroot_prompt # Generate the initpcio send -- "mkinitcpio -P\r" expect \$chroot_prompt send -- "cp /etc/pacman.conf /etc/pacman.conf.orig\r" expect \$chroot_prompt if {$disable_keyring_checks == 0} { # Update the keyring # This may currently currently take a long time # see https://www.reddit.com/r/archlinux/comments/rbjbcr/pacman_keyring_update_taking_too_long/ send -- "pacman-key --init\r" expect \$chroot_prompt send -- "pacman-key --populate archlinux\r" expect \$chroot_prompt send -- "pacman-key --refresh-keys\r" expect \$chroot_prompt } else { # Disable signature check on pacman.conf send -- "sed -i /etc/pacman.conf -r -e 's/^(SigLevel\\\\s*=\\\\s*).*$/\\\\1 Never/g'\r" expect \$chroot_prompt } # Install extra packages send -- {echo "$packages" | pacman -S --noconfirm -} send -- "\r" expect \$chroot_prompt # Install syslinux send -- "pacman -S --noconfirm syslinux\r" expect \$chroot_prompt send -- "syslinux-install_update -i -a -m\r" #expect \$chroot_prompt #send -- "dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/bios/mbr.bin of=/dev/sda\r" expect \$chroot_prompt send -- "sed -i /boot/syslinux/syslinux.cfg -e '1i SERIAL 0 115200'\r" expect \$chroot_prompt send -- "sed -i /boot/syslinux/syslinux.cfg -e 's|APPEND root=/dev/sda3|APPEND console=tty0 console=ttyS0,115200 root=/dev/sda1|g'\r" expect \$chroot_prompt # Set the root password send -- "passwd\r" expect "New password: " send -- "$root_password\r" expect "Retype new password: " send -- "$root_password\r" expect \$chroot_prompt # Create a non-admin user send -- "useradd -d /home/$username -m $username\r" expect \$chroot_prompt send -- "passwd $username\r" expect "New password: " send -- "$user_password\r" expect "Retype new password: " send -- "$user_password\r" expect \$chroot_prompt # Enable the dhcpcd service send -- "ln -sf /usr/lib/systemd/system/dhcpcd.service /etc/systemd/system/multi-user.target.wants/dhcpcd.service\r" expect \$chroot_prompt # Enable and configure the SSH daemon send -- "ln -sf /usr/lib/systemd/system/sshd.service /etc/systemd/system/multi-user.target.wants/sshd.service\r" expect \$chroot_prompt send -- "cat <<_EOF_ >> /etc/ssh/sshd_config RSAAuthentication yes PubkeyAuthentication yes _EOF_ " expect \$chroot_prompt # Copy the user SSH key send -- "mkdir -p /home/$username/.ssh\r" expect \$chroot_prompt send -- {echo "$ssh_pubkey" >> "/home/$username/.ssh/authorized_keys"} send -- "\r" expect \$chroot_prompt send -- {echo "$ssh_privkey" > "/home/$username/.ssh/$(basename $ssh_keyfile)"} send -- "\r" expect \$chroot_prompt send -- {chmod 0600 "/home/$username/.ssh/$(basename $ssh_keyfile)"} send -- "\r" expect \$chroot_prompt send -- {echo "$ssh_pubkey" > "/home/$username/.ssh/$(basename $ssh_keyfile).pub"} send -- "\r" expect \$chroot_prompt send -- {chown -R $username "/home/$username/.ssh"} send -- "\r" expect \$chroot_prompt # Run any post-install scripts send -- {$post_install} send -- "\r" expect \$chroot_prompt # Restore the original pacman.conf send -- "mv /etc/pacman.conf.orig /etc/pacman.conf\r" expect \$chroot_prompt # Clear the pacman cache send -- "rm -rf /var/cache/pacman/pkg/*\r" expect \$chroot_prompt # Exit and shutdown send -- "exit\r" expect \$prompt send -- "umount /mnt\r" expect \$prompt send -- "shutdown -h now\r" expect eof system {echo -e "\n${GREEN}Arch Linux system installed under ${imgfile}${NORMAL}"} EOF echo "--- Log closed at $(date)" >> "$logfile" } function install() { optstring=':o:a:s:m:h:P:u:p:z:l:M:K' [[ "$1" == '--help' ]] && install_usage while getopts ${optstring} arg; do case ${arg} in o) imgfile="${OPTARG}";; a) architecture="${OPTARG}";; s) disk_size="${OPTARG}";; m) memory="${OPTARG}";; h) hostname="${OPTARG}";; P) root_password="${OPTARG}";; u) username="${OPTARG}";; p) user_password="${OPTARG}";; z) timezone="${OPTARG}";; l) locale="${OPTARG}";; M) img_download_page="${OPTARG}";; K) disable_keyring_checks=1;; ?) echo "Invalid option: -${OPTARG}" >&2 install_usage;; esac done [ -z "$imgfile" ] && read -p "Output disk image file [$default_imgfile]: " imgfile [ -z "$architecture" ] && read -p "Architecture [$default_architecture]: " architecture [ -z "$disk_size" ] && read -p "Disk size [$default_disk_size]: " disk_size [ -z "$memory" ] && read -p "Memory in KB [$default_memory]: " memory [ -z "$hostname" ] && read -p "Hostname [$default_hostname]: " hostname [ -z "$root_password" ] && read -sp "Root password [$default_root_password]: " root_password && echo [ -z "$username" ] && read -p "Non-admin username [$default_username]: " username [ -z "$user_password" ] && read -sp "Non-admin user password [$default_user_password]: " user_password && echo [ -z "$timezone" ] && read -p "Timezone [$default_timezone]: " timezone [ -z "$locale" ] && read -p "Locale [$default_locale]: " locale for var in imgfile \ architecture \ disk_size \ memory \ hostname \ root_password \ username \ user_password \ timezone \ locale do default_var=default_$var [ -z "${!var}" ] && declare ${var}=${!default_var} export $var done download_latest_arch_iso create_disk_image install_os } ################ ################## # Main script function usage() { actions=(install) cat < Run $(basename "$0") --help for more details. Available actions: $actions EOF exit 1 } function main() { action=$1 shift case "$action" in install) install $*;; *) usage;; esac } main $* ##################