#!/usr/bin/env bash set -o nounset set -o errexit set -o pipefail tmpdir="$(mktemp -d --tmpdir=/var/tmp)" trap cleanup EXIT ISO_MIRROR="https://ftp.fau.de/archlinux/iso/latest/" ISO_MIRROR="https://ftp.acc.umu.se/mirror/archlinux/iso/latest/" iso_dir="${XDG_DATA_HOME}/arch-iso/" iso_path="${iso_dir}/archlinux-x86_64.iso" cleanup() { rm -rf "${tmpdir}" pids=() jobs -p | while IFS="" read -r line; do pids+=("$line"); done kill "${pids[@]}" } download_iso() { mkdir -p "${iso_dir}" ( cd "${iso_dir}" wget \ --timestamping \ --no-hsts \ "${ISO_MIRROR}sha256sums.txt" if [[ ! -e "${iso_path}" ]] || ! sha256sum --ignore-missing --check ./sha256sums.txt; then wget \ --no-hsts \ --output-document "${iso_path}" \ "${ISO_MIRROR}archlinux-x86_64.iso" fi ) } disk="${tmpdir}/disk.qcow2" mon_sock="${tmpdir}/mon.sock" sshopts=( -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PreferredAuthentications=publickey -o ConnectTimeout=1s -i "${tmpdir}/ssh.key" -l root -p 60022 127.0.0.1 ) wait_for_ssh() { echo "waiting for ssh" set +o errexit maxtries=60 tries=0 while ! ssh -q "${sshopts[@]}" true; do ((tries++)) if ((tries > maxtries)); then echo "ssh did not become available" exit 3 fi sleep 1 done echo "ssh available" set -o errexit } qemuopts=( "-m" "size=8G" "-drive" "file=${disk},format=qcow2,if=none,id=root" "-accel" "kvm" "-drive" "if=pflash,format=raw,readonly=true,file=/usr/share/ovmf/x64/OVMF_CODE.fd" "-drive" "if=pflash,format=raw,file=${tmpdir}/efivars.fd" "-machine" "q35,smm=on,acpi=on" "-smp" "cpus=8,sockets=1,cores=8,threads=1" "-cpu" "host" "-netdev" "user,id=net0,hostfwd=tcp::60022-:22" "-device" "virtio-net-pci,netdev=net0" "-nodefaults" "-vga" "virtio" "-display" "spice-app" ) send_mon() { local socket="${1}" patterns=( -e 's/ /spc/' -e 's/\./dot/' -e 's/,/comma/' -e 's/-/slash/' -e 's/\//shift-7/' -e 's/\([A-Z]\)/shift-\L\1/' -e 's/=/shift-0/' -e 's/"/shift-2/' -e "s/'/shift-0x2b/" # ^ is a dead key, we would have to send a space to be precise. but it's # going to work out as long as the following char does not combine -e 's/\^/0x29/' -e 's/#/0x2b/' -e 's/\?/shift-0x0c/' -e 's/\\/alt_r-0x0c/' # altgr is alt_r -e 's/\*/shift-0x1b/' -e 's/(/shift-0x09/' -e 's/)/shift-0x0a/' -e 's/^/sendkey /' ) cat \ <(fold -w 1 | sed "${patterns[@]}") \ <(echo "sendkey ret") | nc -N -U "${socket}" echo "sendkey ret" | nc -N -U "${socket}" } install_from_iso() { local hostname="${1}" shift local hostqemuopts=("$@") rm -rf "${tmpdir:?}"/* ssh-keygen -f "${tmpdir}"/ssh.key -N '' -t ed25519 -C 'archiso-tmp' cloud-localds "${tmpdir}/userdata.img" <( cat < /tmp/specials.sh if [[ "\\\$(tty)" == "/dev/tty1" ]] ; then mkdir /var/cache/pacman-cache-host mount -t 9p -o trans=virtio,version=9p2000.L,ro pacman-cache /var/cache/pacman-cache-host # Uncomment CacheDir and prepend the host pacman cache as cachedir # At worst, the cache directory will be ignored if it does not exist # Pacman will always use the first directory with write access for downloads sed -i 's/^#\?\(CacheDir.*\)/\1\nCacheDir = \/var\/cache\/pacman-cache-host\//' /etc/pacman.conf fi SPECIALS mv /mnt/root/.bash_profile /tmp/rest.sh cat /tmp/specials.sh /tmp/rest.sh > /mnt/root/.bash_profile rsync -rl /repo/ /mnt/var/lib/dotfiles/ umount /mnt poweroff EOF wait } configure_new_system() { local hostname="${1}" shift local hostqemuopts=("${@}") opts=( "-fsdev" "local,id=pacman-cache,path=share,path=/var/cache/pacman/pkg/,readonly=on,security_model=none" "-device" "virtio-9p-pci,fsdev=pacman-cache,mount_tag=pacman-cache" "-monitor" "unix:${mon_sock},server=on,wait=off" ) qemu-system-x86_64 -name "${hostname}" "${qemuopts[@]}" "${hostqemuopts[@]}" "${opts[@]}" & # 5s for grub timeout, 5s for kernel boot echo waiting for luks password prompt ... sleep 10s echo 'lukspw' | send_mon "${mon_sock}" echo waiting for boot ... sleep 10s wait } machines=(ares neptune) if (($# > 0)); then machines=("${@}") fi download_iso for hostname in "${machines[@]}"; do case "${hostname}" in ares) hostqemuopts=("-device" "ide-hd,drive=root") ;; neptune) hostqemuopts=("-device" "nvme,serial=rootnvme,drive=root") ;; *) exit 1 ;; esac [[ ! "${hostqemuopts[*]}" ]] && exit 1 install_from_iso "${hostname}" "${hostqemuopts[@]}" configure_new_system "${hostname}" "${hostqemuopts[@]}" done