diff --git a/README b/README index 1d08733..a2d91f6 100644 --- a/README +++ b/README @@ -35,8 +35,8 @@ The creation hooks currently supported are: ethernet-dhcp: this hook will setup ethernet in the VM by enabling systemd-resolved and - symlinking /etc/resolv.conf properly, as well as creating and enabling a - netctl configuration based on the ethernet-dhcp example. + openresolv properly, as well as creating and enabling a systemd-networkd + configuration. virtual machine boot -------------------- @@ -62,3 +62,13 @@ memory to the machine, by specifying additional qemu parameters on the command line following the virtual machine image name: $> DISPLAY= ./pvmboot [path to image] -m 2G + +release tarball creation +------------------------ + +To convert a virtual machine image into a parabola release tarball, run: + + $> ./pvm2tarball.sh [path to image] + +This will attempt to mount the rootfs and the /boot partition, and tar the +contents to an output file, filtering out all unneeded files. diff --git a/src/hooks/hook-ethernet-dhcp.sh b/src/hooks/hook-ethernet-dhcp.sh index 8af9661..2e87552 100644 --- a/src/hooks/hook-ethernet-dhcp.sh +++ b/src/hooks/hook-ethernet-dhcp.sh @@ -2,16 +2,19 @@ set -e -# setup systemd-resolved -systemctl start systemd-resolved.service || return -systemctl enable systemd-resolved.service -ln -sf /var/run/systemd/resolve/resolv.conf /etc/resolv.conf - # determine first ethernet device eth="$(basename "$(find /sys/class/net/ -mindepth 1 -maxdepth 1 -iname 'e*' | head -n1)")" [ -n "$eth" ] || eth="eth0" -# setup netctl for ethernet-dhcp -sed "s/eth0/$eth/" /etc/netctl/examples/ethernet-dhcp > /etc/netctl/ethernet-dhcp -netctl start ethernet-dhcp || return -netctl enable ethernet-dhcp +# create a network configuration +cat > /etc/systemd/network/$eth.network << EOF +[Match] +Name=$eth + +[Network] +DHCP=yes +EOF + +# enable said network configuration +systemctl enable systemd-networkd.service +systemctl enable systemd-resolved.service diff --git a/src/pvm2tarball.sh b/src/pvm2tarball.sh new file mode 100644 index 0000000..7eb74da --- /dev/null +++ b/src/pvm2tarball.sh @@ -0,0 +1,165 @@ +#!/bin/bash +############################################################################### +# parabola-vmbootstrap -- create and start parabola virtual machines # +# # +# Copyright (C) 2017 - 2019 Andreas Grapentin # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +############################################################################### + +# shellcheck source=/usr/lib/libretools/messages.sh +. "$(librelib messages)" + +usage() { + print "usage: %s [-h] [-o FILE] IMG" "${0##*/}" + prose "Produce a parabola release tarball from IMG." + echo + prose "IMG is expected to be a valid parabola image, ideally freshly bootstrapped + using pvmbootstrap. If FILE is not specifed, generate an archive name + from IMG and place it in the current working directory" + echo + echo "Supported options:" + echo " -o FILE Write the generated tar archive to FILE instead of" + echo " generating a name for the archive from IMG" + echo " -h Display this help and exit" + echo + echo "This script is part of parabola-vmbootstrap. source code available at:" + echo " " +} + +pvm_mount() { + if ! file "$1" | grep -q ' DOS/MBR '; then + error "%s: does not seem to be a raw qemu image." "$1" + return "$EXIT_FAILURE" + fi + + trap 'pvm_umount' INT TERM EXIT + + workdir="$(mktemp -d -t pvm-XXXXXXXXXX)" || return + loopdev="$(sudo losetup -fLP --show "$1")" || return + + # find the root partition + local part rootpart + for part in "$loopdev"p*; do + sudo mount "$part" "$workdir" || continue + if [ -f "$workdir"/etc/fstab ]; then + rootpart="$part" + break + fi + sudo umount "$workdir" + done + + if [ -z "$rootpart" ]; then + error "%s: unable to determine root partition." "$1" + return "$EXIT_FAILURE" + fi + + # find the boot partition + bootpart="$(findmnt -senF "$workdir"/etc/fstab /boot | awk '{print $2}')" + + if [ -z "$bootpart" ]; then + error "%s: unable to determine boot partition." "$1" + return "$EXIT_FAILURE" + fi + + # mount and be happy + sudo mount "$bootpart" "$workdir"/boot || return +} + +pvm_umount() { + trap - INT TERM EXIT + + [ -n "$workdir" ] && (sudo umount -R "$workdir"; rmdir "$workdir") + unset workdir + [ -n "$loopdev" ] && sudo losetup -d "$loopdev" + unset loopdev +} + +main() { + if [ "$(id -u)" -eq 0 ]; then + error "This program must be run as a regular user" + exit "$EXIT_NOPERMISSION" + fi + + # parse options + local output + while getopts 'ho:' arg; do + case "$arg" in + h) usage; return "$EXIT_SUCCESS";; + o) output="$OPTARG";; + *) usage >&2; exit "$EXIT_INVALIDARGUMENT";; + esac + done + local shiftlen=$(( OPTIND - 1 )) + shift $shiftlen + if [ "$#" -ne 1 ]; then usage >&2; exit "$EXIT_INVALIDARGUMENT"; fi + + local imagebase imagefile="$1" + imagebase="$(basename "$imagefile")" + shift + + # check for input file presence + if [ ! -e "$imagefile" ]; then + error "%s: file not found" "$imagefile" + exit "$EXIT_FAILURE" + fi + + # determine output file + [ -n "$output" ] || output="${imagebase%.*}.tar.gz" + + # check for output file presence + if [ -e "$output" ]; then + warning "%s: file exists. Continue? [y/N]" "$output" + read -p " " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + exit "$EXIT_FAILURE" + fi + rm -f "$output" || exit + fi + + # mount the root filesystem + local workdir loopdev + pvm_mount "$imagefile" || exit + + # tar the root filesystem, excluding unneeded things + sudo tar -c -f "$output" -C "$workdir" -X - . << EOF +./boot/lost+found +./etc/.updated +./etc/pacman.d/empty.conf +./etc/pacman.d/gnupg +./lost+found +./root/.bash_history +./var/.updated +./var/log/journal/* +./var/log/pacman.log +./var/log/tallylog +EOF + + # HACKING: + # to update the exclude list above, one can download the latest archlinuxarm + # release tarball, and scroll over the diff generated by running both the + # archlinuxarm and the generated parabola tarball through: + # + # `tar -tf | sort` + + # give the archive back to the user + sudo chown "$(id -u)":"$(id -g)" "$output" + + # cleanup + pvm_umount +} + +main "$@" + diff --git a/src/pvmboot.sh b/src/pvmboot.sh index 0c89e37..ab9ba17 100644 --- a/src/pvmboot.sh +++ b/src/pvmboot.sh @@ -22,22 +22,32 @@ . "$(librelib messages)" usage() { - print "usage: %s [-h] filename [args...]" "${0##*/}" + print "usage: %s [-h] IMG [ARG]..." "${0##*/}" + prose "Determine the architecture of IMG and boot it using qemu. IMG is assumed + to be a valid, raw-formatted parabola virtual machine image, ideally + created using pvmbootstrap. The started instances are assigned 1GiB of + RAM and one SMP core." echo - prose " this script is designed to smartly boot a parabola GNU/Linux-libre - virtual machine with qemu. It takes the path to a virtual machine image - as parameter, and determines the architecture of that image. It sets - default qemu parameters for the target architecture, and determines - whether kvm acceleration is available." + prose "When a graphical desktop environment is available, start the machine + normally, otherwise append -nographic to the qemu options. This behavior + can be forced by unsetting DISPLAY manually, for example through:" echo - prose " the script also determines whether a graphical desktop environment - is available by evaluating the DISPLAY environment variable, and sets - default options accordingly." + echo " DISPLAY= ${0##*/} IMG ..." echo - prose " the default qemu parameters can be overwritten and extended by adding - custom arguments after the image file name." + prose "When the architecture of IMG is compatible with the host architecture, + append -enable-kvm to the qemu arguments." echo - echo "this script is developed as part of parabola-vmbootstrap." + prose "Further arguments provided after IMG will be passed unmodified to the + qemu invocation. This can be used to allocate more resources to the virtual + machine, for example:" + echo + echo " ${0##*/} IMG -m 2G -smp 2" + echo + echo "Supported options:" + echo " -h Display this help and exit" + echo + echo "This script is part of parabola-vmbootstrap. source code available at:" + echo " " } pvm_mount() { diff --git a/src/pvmbootstrap.sh b/src/pvmbootstrap.sh index b964ef3..cda13c0 100644 --- a/src/pvmbootstrap.sh +++ b/src/pvmbootstrap.sh @@ -22,26 +22,31 @@ . "$(librelib messages)" usage() { - print "usage: %s [-h] [-s size] [-M mirror] [-H hook...] filename arch" "${0##*/}" + print "usage: %s [-h] [-s SIZE] [-M MIRROR] [-H HOOK]... IMG ARCH" "${0##*/}" + prose "Produce preconfigured parabola GNU/Linux-libre virtual machine instances." echo - prose " this script produces preconfigured parabola GNU/Linux-libre virtual - machine images, to be started using the accompanying pvmboot.sh - script. The created image is placed at the specified path." + prose "The produced image file is written to IMG, and is configured and + bootstrapped for the achitecture specified in ARCH. ARCH can ether be + one of the officially supported architectures x86_64, i686 or armv7h, + or one of the unofficial arches ppc64le and riscv64 (refer to -M for + custom package mirrors)" echo - prose " the target architecture of the virtual machine image can be one of the - officially supported i686, x86_64 and armv7h, as well as one of the - unofficial ports for ppc64le and riscv64." + echo "Supported options:" + echo " -s SIZE Set the size of the VM image (default: 64GiB)" + echo " -M MIRROR Choose a different mirror to pacstrap from" + echo " default: " + echo " -H HOOK Enable a hook to customize the created image. This can be" + echo " the path to a script, which will be executed once within" + echo " the running VM, or one of the predefined hooks described" + echo " below. This option can be specified multiple times." + echo " -h Display this help and exit" echo - prose " the creation of the virtual machine is configurable in three ways. First, - the size of the image can be configured with the -s switch, whose - value is passed verbatim to qemu-img create (default 64G). Second, - the mirror to load packages from can be configured with the -M switch, - wich is necessary for the unofficial ports, whose packages are not on - the main mirrors. Lastly, the -H switch can be passed multiple times to - specify post-install hooks to be run in the virtual machine for - install finalization. See src/hooks/ for examples." + echo "Predefined hooks:" + echo " ethernet-dhcp: configure and enable an ethernet device in the virtual" + echo " machine, using openresolv, dhcp and systemd-networkd" echo - echo "this script is developed as part of parabola-vmbootstrap." + echo "This script is part of parabola-vmbootstrap. source code available at:" + echo " " } pvm_native_arch() { @@ -170,10 +175,12 @@ Server = $mirror Server = $mirror [community] Server = $mirror +[pcr] +Server = $mirror EOF # prepare lists of packages - local pkg=(base haveged) + local pkg=(base haveged openssh openresolv ldns net-tools) case "$arch" in i686|x86_64) pkg+=(grub) ;; esac @@ -190,6 +197,13 @@ EOF sudo swapoff "$swapdev" sudo swapon --all + # produce a hostname + echo "parabola" | sudo tee "$workdir"/etc/hostname + + # produce an /etc/locale.conf + echo "LANG=en_US.UTF-8" | sudo tee "$workdir"/etc/locale.conf + sudo sed -i 's/#en_US.UTF-8/en_US.UTF-8/' "$workdir"/etc/locale.gen + # install a boot loader case "$arch" in i686|x86_64) @@ -242,21 +256,29 @@ EOF #!/bin/bash systemctl disable preinit.service +# generate the locale +locale-gen + # fix the mkinitcpio mkinitcpio -p linux-libre # fix ca-certificates pacman -U --noconfirm /var/cache/pacman/pkg/ca-certificates-utils-*.pkg.tar.xz +# run the hooks for hook in /root/hooks/*; do echo "running hook \"$hook\"" . "$hook" || return done +# clean up after yourself rm -rf /root/hooks rm -f /root/hooks.sh rm -f /usr/lib/systemd/system/preinit.service +rm -f /var/cache/pacman/pkg/* +rm -f /root/.bash_history +# report success :) echo "preinit hooks successful" EOF