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