housekeeping

This commit is contained in:
bill-auger 2019-12-19 00:15:37 -05:00
parent 164b5b98b9
commit 68fb5be67a
3 changed files with 155 additions and 175 deletions

@ -19,10 +19,10 @@
###############################################################################
# shellcheck source=/usr/lib/libretools/messages.sh
. "$(librelib messages)"
source "$(librelib messages)"
usage() {
print "usage: %s [-h] [-o FILE] IMG" "${0##*/}"
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
@ -39,18 +39,18 @@ usage() {
}
pvm_mount() {
if ! file "$1" | grep -q ' DOS/MBR '; then
error "%s: does not seem to be a raw qemu image." "$1"
if ! file "$imagefile" | grep -q ' DOS/MBR '; then
error "%s: does not seem to be a raw qemu image." "$imagefile"
return "$EXIT_FAILURE"
fi
trap 'pvm_umount' INT TERM EXIT
workdir="$(mktemp -d -t pvm-XXXXXXXXXX)" || return
loopdev="$(sudo losetup -fLP --show "$1")" || return
loopdev="$(sudo losetup -fLP --show "$imagefile")" || return
# find the root partition
local part rootpart
local part rootpart bootpart
for part in "$loopdev"p*; do
sudo mount "$part" "$workdir" || continue
if [ -f "$workdir"/etc/fstab ]; then
@ -61,7 +61,7 @@ pvm_mount() {
done
if [ -z "$rootpart" ]; then
error "%s: unable to determine root partition." "$1"
error "%s: unable to determine root partition." "$imagefile"
return "$EXIT_FAILURE"
fi
@ -69,7 +69,7 @@ pvm_mount() {
bootpart="$(findmnt -senF "$workdir"/etc/fstab /boot | awk '{print $2}')"
if [ -z "$bootpart" ]; then
error "%s: unable to determine boot partition." "$1"
error "%s: unable to determine boot partition." "$imagefile"
return "$EXIT_FAILURE"
fi
@ -105,8 +105,8 @@ main() {
shift $shiftlen
if [ "$#" -ne 1 ]; then usage >&2; exit "$EXIT_INVALIDARGUMENT"; fi
local imagebase imagefile="$1"
imagebase="$(basename "$imagefile")"
local image_filename imagefile="$1"
image_filename="$(basename "$imagefile")"
shift
# check for input file presence
@ -116,7 +116,7 @@ main() {
fi
# determine output file
[ -n "$output" ] || output="${imagebase%.*}.tar.gz"
[ -n "$output" ] || output="${image_filename%.img*}.tar.gz"
# check for output file presence
if [ -e "$output" ]; then
@ -131,9 +131,15 @@ main() {
# mount the root filesystem
local workdir loopdev
pvm_mount "$imagefile" || exit
pvm_mount || exit
# tar the root filesystem, excluding unneeded things
# HACKING:
# to update the exclude list, 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 <tarball> | sort`
sudo tar -c -f "$output" -C "$workdir" -X - . << EOF
./boot/lost+found
./etc/.updated
@ -146,13 +152,6 @@ main() {
./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 <tarball> | sort`
# give the archive back to the user
sudo chown "$(id -u)":"$(id -g)" "$output"
@ -161,4 +160,3 @@ EOF
}
main "$@"

@ -19,29 +19,30 @@
###############################################################################
# shellcheck source=/usr/lib/libretools/messages.sh
. "$(librelib messages)"
source "$(librelib messages)"
usage() {
print "usage: %s [-h] IMG [ARG]..." "${0##*/}"
prose "Determine the architecture of IMG and boot it using qemu. IMG is assumed
print "USAGE: %s [-h] <img> [qemu-args ...]" "${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
created using pvmbootstrap. The started instance is assigned 1G of
RAM and one SMP core."
echo
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
echo " DISPLAY= ${0##*/} IMG ..."
echo " DISPLAY= ${0##*/} ./an.img"
echo
prose "When the architecture of IMG is compatible with the host architecture,
prose "When the architecture of <img> is compatible with the host architecture,
append -enable-kvm to the qemu arguments."
echo
prose "Further arguments provided after IMG will be passed unmodified to the
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 " ${0##*/} ./an.img -m 2G -smp 2"
echo
echo "Supported options:"
echo " -h Display this help and exit"
@ -124,38 +125,36 @@ pvm_guess_qemu_args() {
# if we're running a supported arch, enable kvm
if pvm_native_arch "$2"; then qemu_args+=(-enable-kvm); fi
# otherwise, decide by target arch
# set arch-specific args
local kernel_console
case "$2" in
i386|x86_64|ppc64)
qemu_args+=(-m 2G "$1")
qemu_args+=(-m 1G -hda "$1")
# unmount the unneeded virtual drive early
pvm_umount ;;
arm)
qemu_args+=(
-machine virt
-m 2G
kernel_console="console=tty0 console=ttyAMA0 "
qemu_args+=(-machine virt
-m 1G
-kernel "$workdir"/vmlinuz-linux-libre
-initrd "$workdir"/initramfs-linux-libre.img
-append "console=tty0 console=ttyAMA0 rw root=/dev/vda3"
-append "${kernel_console}rw root=/dev/vda3"
-drive "if=none,file=$1,format=raw,id=hd"
-device "virtio-blk-device,drive=hd"
-netdev "user,id=mynet"
-device "virtio-net-device,netdev=mynet") ;;
riscv64)
qemu_args+=(
-machine virt
-m 2G
kernel_console=$( [ -z "$DISPLAY" ] && echo "console=ttyS0 " )
qemu_args+=(-machine virt
-m 1G
-kernel "$workdir"/bbl
-append "rw root=/dev/vda"
-append "${kernel_console}rw root=/dev/vda"
-drive "file=${loopdev}p3,format=raw,id=hd0"
-device "virtio-blk-device,drive=hd0"
-object "rng-random,filename=/dev/urandom,id=rng0"
-device "virtio-rng-device,rng=rng0"
-device "virtio-net-device,netdev=usernet"
-netdev "user,id=usernet")
if [ -z "$DISPLAY" ]; then
qemu_args+=(-append "console=ttyS0 rw root=/dev/vda");
fi ;;
-netdev "user,id=usernet"
-device "virtio-net-device,netdev=usernet") ;;
*)
error "%s: unable to determine default qemu args" "$1"
return "$EXIT_FAILURE" ;;
@ -177,15 +176,9 @@ main() {
done
local shiftlen=$(( OPTIND - 1 ))
shift $shiftlen
if [ "$#" -lt 1 ]; then usage >&2; exit "$EXIT_INVALIDARGUMENT"; fi
local imagefile="$1"
shift
if [ ! -e "$imagefile" ]; then
error "%s: file not found" "$imagefile"
exit "$EXIT_FAILURE"
fi
[ ! -e "$imagefile" ] && error "%s: file not found" "$imagefile" && exit "$EXIT_FAILURE"
local workdir loopdev
pvm_mount "$imagefile" || exit

@ -18,44 +18,52 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
###############################################################################
# shellcheck source=/usr/lib/libretools/messages.sh
. "$(librelib messages)"
source "$(librelib messages)"
readonly DEF_MIRROR="https://repo.parabola.nu/\$repo/os/\$arch"
readonly DEF_IMG_GB=64
Hooks=()
Mirror=$DEF_MIRROR
ImgSizeGb=$DEF_IMG_GB
Init=''
usage() {
print "usage: %s [-hO] [-s SIZE] [-M MIRROR] [-H HOOK]... IMG ARCH" "${0##*/}"
print "USAGE:"
print " pvmbootstrap [-h] [-H <hook>] [-M <mirror>] [-O] [-s <img_size>] <img> <arch>"
echo
prose "Produce preconfigured parabola GNU/Linux-libre virtual machine instances."
echo
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)"
prose "The produced image file is written to <img>, and is configured and
bootstrapped for the achitecture specified in <arch>. <arch> can be any
one of the supported architectures: 'x86_64', 'i686' or 'armv7h',
or one of the experimental arches: 'ppc64le' or 'riscv64'."
echo
echo "Supported options:"
echo " -O bootstrap an openrc system instead of a systemd one"
echo " -s SIZE Set the size of the VM image (default: 64GiB)"
echo " -M MIRROR Choose a different mirror to pacstrap from"
echo " default: <https://repo.parabola.nu/\$repo/os/\$arch>"
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 " -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 pre-defined hooks described"
echo " below. This option can be specified multiple times."
echo " -M <mirror> Specify a different mirror from which to fetch packages"
echo " (default: $DEF_MIRROR)"
echo " -O Bootstrap an openrc system instead of a systemd one"
echo " -s <img_size> Set the size (in GB) of the VM image (default: $DEF_IMG_GB)"
echo
echo "Predefined hooks:"
echo " ethernet-dhcp: configure and enable an ethernet device in the virtual"
echo " machine, using openresolv, dhcp and systemd-networkd"
echo "Pre-defined hooks:"
echo " ethernet-dhcp: Configure and enable an ethernet device in the virtual"
echo " machine, using openresolv, dhcpcd, and systemd-networkd"
echo
echo "This script is part of parabola-vmbootstrap. source code available at:"
echo " <https://git.parabola.nu/~oaken-source/parabola-vmbootstrap.git>"
}
pvm_native_arch() {
local arch
case "$1" in
arm*) arch=armv7l;;
*) arch="$1";;
esac
local arch=$( [[ "$1" =~ arm.* ]] && echo 'armv7l' || echo "$1" )
setarch "$arch" /bin/true 2>/dev/null || return
}
@ -64,7 +72,7 @@ pvm_bootstrap() {
msg "%s: starting image creation for %s" "$file" "$arch"
# create the raw image file
qemu-img create -f raw "$file" "$size" || return
qemu-img create -f raw "$file" "${ImgSizeGb}G" || return
# prepare for cleanup
trap 'pvm_cleanup' INT TERM RETURN
@ -73,8 +81,6 @@ pvm_bootstrap() {
local workdir loopdev
workdir="$(mktemp -d -t pvm-rootfs-XXXXXXXXXX)" || return
loopdev="$(sudo losetup -fLP --show "$file")" || return
# clean out the first 8MiB
sudo dd if=/dev/zero of="$loopdev" bs=1M count=8 || return
# partition
@ -149,11 +155,10 @@ pvm_bootstrap() {
*) qemu_arch="$arch" ;;
esac
if [[ -z $(sudo grep -l -F \
-e "interpreter /usr/bin/qemu-$qemu_arch-" \
-r -- /proc/sys/fs/binfmt_misc 2>/dev/null \
| xargs -r sudo grep -xF 'enabled') ]]
then
local qemu_user_static=$(sudo grep -l -F -e "interpreter /usr/bin/qemu-$qemu_arch-" \
-r -- /proc/sys/fs/binfmt_misc 2>/dev/null | \
xargs -r sudo grep -xF 'enabled' )
if [[ -z "$qemu_user_static" ]]; then
error "%s: missing qemu-user-static for %s" "$file" "$arch"
return "$EXIT_FAILURE"
fi
@ -163,51 +168,42 @@ pvm_bootstrap() {
fi
# prepare pacstrap config
local pacconf
local pacconf repos
pacconf="$(mktemp -t pvm-pacconf-XXXXXXXXXX)" || return
cat > "$pacconf" << EOF
[options]
Architecture = $arch
[libre]
Server = $mirror
[core]
Server = $mirror
[extra]
Server = $mirror
[community]
Server = $mirror
[pcr]
Server = $mirror
EOF
repos=('libre' 'core' 'extra' 'community' 'pcr')
echo -e "[options]\nArchitecture = $arch\n\n" > "$pacconf"
for repo in ${repos[@]}; do echo -e "[$repo]\nServer = $Mirror\n" >> "$pacconf"; done;
# prepare lists of packages
local pkg=("base$init" "openssh$init" openresolv ldns)
# FIXME: [nonsystemd] is in a transitional phase
# package name suffices such as '-opernrc' will be removed soon
# ideally, the install process will be identical for either systemd or openrc
# depending only on which repos are enabled
local pkgs=("base$Init" "openssh$Init" openresolv ldns)
case "$arch" in
i686|x86_64) pkg+=(grub) ;;
i686|x86_64) pkgs+=(grub) ;;
esac
case "$arch" in
riscv64) ;;
*) pkg+=("haveged$init" net-tools) ;;
*) pkgs+=("haveged$Init" net-tools) ;;
esac
# Add back in base packages that the switch from base group
if [[ ! $Init ]]; then
# FIXME: Add back in base packages that the switch from base group
# to base PKG yanked and be specific on OpenRC to avoid conflicts
if [[ ! $init ]]; then
pkg+=(cryptsetup device-mapper dhcpcd e2fsprogs inetutils \
# ideally, we will assign these to a new 'base-extras' group
pkgs+=(cryptsetup device-mapper dhcpcd e2fsprogs inetutils \
jfsutils linux-libre logrotate lvm2 man-db mdadm nano \
netctl pacman-mirrorlist perl reiserfsprogs s-nail sysfsutils \
texinfo usbutils vi xfsprogs your-freedom systemd-udev \
systemd-libudev)
systemd-libudev )
else
pkg+=(systemd-libs-dummy)
pkgs+=(systemd-libs-dummy)
fi
local pkg_guest_cache=(ca-certificates-utils)
# pacstrap! :)
sudo pacstrap -GMc -C "$pacconf" "$workdir" "${pkg[@]}" || return
sudo pacstrap -GMc -C "$pacconf" "$workdir" "${pkgs[@]}" || return
sudo pacstrap -GM -C "$pacconf" "$workdir" "${pkg_guest_cache[@]}" || return
# create an fstab
@ -231,35 +227,35 @@ EOF
# install a boot loader
case "$arch" in
i686|x86_64)
local grub_def_file="$workdir"/etc/default/grub
local grub_cfg_file=/boot/grub/grub.cfg
# enable serial console
local field=GRUB_CMDLINE_LINUX_DEFAULT
local value="console=tty0 console=ttyS0"
sudo sed -i "s/.*$field=.*/$field=\"$value\"/" \
"$workdir"/etc/default/grub || return
sudo sed -i "s/.*$field=.*/$field=\"$value\"/" "$grub_def_file" || return
# disable boot menu timeout
local field=GRUB_TIMEOUT
local value=0
sudo sed -i "s/.*$field=.*/$field=$value/" \
"$workdir"/etc/default/grub || return
sudo sed -i "s/.*$field=.*/$field=$value/" "$grub_def_file" || return
# install grub to the VM
sudo arch-chroot "$workdir" grub-install --target=i386-pc "$loopdev" || return
sudo arch-chroot "$workdir" grub-mkconfig -o /boot/grub/grub.cfg || return
sudo arch-chroot "$workdir" grub-install "$loopdev" || return
sudo arch-chroot "$workdir" grub-mkconfig -o $grub_cfg_file || return
;;
riscv64)
# FIXME: for the time being, use fedora bbl to boot
sudo wget https://fedorapeople.org/groups/risc-v/disk-images/bbl \
-O "$workdir"/boot/bbl || return
local bbl_url=https://fedorapeople.org/groups/risc-v/disk-images/bbl
sudo wget $bbl_url -O "$workdir"/boot/bbl || return
;;
# armv7h has no boot loader.
# FIXME: what about ppc64le
esac
# regenerate the initcpio, skipping the autodetect hook
sudo cp "$workdir"/etc/mkinitcpio.d/linux-libre.preset{,.backup} || return
echo "default_options=\"-S autodetect\"" \
| sudo tee -a "$workdir"/etc/mkinitcpio.d/linux-libre.preset || return
local preset_file="$workdir"/etc/mkinitcpio.d/linux-libre.preset
sudo cp "$preset_file"{,.backup} || return
echo "default_options=\"-S autodetect\"" | sudo tee -a "$preset_file" || return
sudo arch-chroot "$workdir" mkinitcpio -p linux-libre || return
sudo mv "$workdir"/etc/mkinitcpio.d/linux-libre.preset{.backup,} || return
sudo mv "$preset_file"{.backup,} || return
# disable audit
sudo arch-chroot "$workdir" systemctl mask systemd-journald-audit.socket
@ -273,7 +269,7 @@ EOF
# push hooks into the image
sudo mkdir -p "$workdir/root/hooks"
[ "${#hooks[@]}" -eq 0 ] || sudo cp -v "${hooks[@]}" "$workdir"/root/hooks/
[ "${#Hooks[@]}" -eq 0 ] || sudo cp -v "${Hooks[@]}" "$workdir"/root/hooks/
# create a master hook script
sudo tee "$workdir"/root/hooks.sh << 'EOF'
@ -292,7 +288,7 @@ 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
source "$hook" || return
done
# clean up after yourself
@ -329,28 +325,26 @@ EOF
# unmount everything
pvm_cleanup
# boot the machine, and run the preinit scripts
# boot the machine to run the preinit hooks
local pvmboot_cmd
local qemu_flags=(-no-reboot)
local pvmboot
if [ -f "./src/pvmboot.sh" ]; then
pvmboot=(bash ./src/pvmboot.sh)
pvmboot_cmd=(bash ./src/pvmboot.sh)
elif type -p pvmboot &>/dev/null; then
pvmboot=(pvmboot)
pvmboot_cmd=('pvmboot')
else
error "%s: pvmboot not available -- unable to run hooks" "$file"
return "$EXIT_FAILURE"
fi
pvmboot_cmd+=("$file" "${qemu_flags[@]}")
exec 3>&1
DISPLAY='' "${pvmboot[@]}" "$file" "${qemu_flags[@]}" \
| tee /dev/fd/3 | grep -q "preinit hooks successful"
msg "booting the machine to run the pre-init hooks"
DISPLAY='' "${pvmboot_cmd[@]}" | tee /dev/fd/3 | grep -q "preinit hooks successful"
local res=$?
exec 3>&-
! (( $res )) || error "%s: failed to complete preinit hooks" "$file"
if [ "$res" -ne 0 ]; then
error "%s: failed to complete preinit hooks" "$file"
return "$res"
fi
return $res
}
pvm_cleanup() {
@ -360,7 +354,7 @@ pvm_cleanup() {
unset pacconf
if [ -n "$workdir" ]; then
sudo rm -f "$workdir"/usr/bin/qemu-*
sudo umount -R "$workdir"
sudo umount -R "$workdir" 2> /dev/null
rmdir "$workdir"
fi
unset workdir
@ -374,34 +368,29 @@ main() {
exit "$EXIT_NOPERMISSION"
fi
local size="64G"
local mirror="https://repo.parabola.nu/\$repo/os/\$arch"
local hooks=()
local init
# parse options
while getopts 'hOs:M:H:' arg; do
case "$arg" in
O) init="-openrc";;
h) usage; return "$EXIT_SUCCESS";;
s) size="$OPTARG";;
M) mirror="$OPTARG";;
H) if [ -e "/usr/lib/libretools/pvmbootstrap/hook-$OPTARG.sh" ]; then
hooks+=("/usr/lib/libretools/pvmbootstrap/hook-$OPTARG.sh")
elif [ -e "./src/hooks/hook-$OPTARG.sh" ]; then
hooks+=("./src/hooks/hook-$OPTARG.sh")
elif [ -e "$OPTARG" ]; then
hooks+=("$OPTARG")
Hooks+=("$OPTARG")
else
warning "%s: hook does not exist" "$OPTARG"
fi ;;
M) Mirror="$OPTARG";;
O) Init="-openrc";;
s) ImgSizeGb="$(sed 's|[^0-9]||g' <<<$OPTARG)";;
*) usage >&2; exit "$EXIT_INVALIDARGUMENT";;
esac
done
local shiftlen=$(( OPTIND - 1 ))
shift $shiftlen
if [ "$#" -ne 2 ]; then usage >&2; exit "$EXIT_INVALIDARGUMENT"; fi
local file="$1"
local arch="$2"