From aece2dad3e16feb6073e17a3b61dfd8d69007ecb Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Wed, 11 Jan 2023 19:25:08 +0100 Subject: move the debvm-* tools to bin The purpose of this change is adding support files to be referenced and called from these tools. Those support files shall be located in ../share and this way of locating them shall work both in-source and when installed. --- bin/debvm-run | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100755 bin/debvm-run (limited to 'bin/debvm-run') diff --git a/bin/debvm-run b/bin/debvm-run new file mode 100755 index 0000000..c2a3f21 --- /dev/null +++ b/bin/debvm-run @@ -0,0 +1,272 @@ +#!/bin/sh +# Copyright 2022 Helmut Grohne +# SPDX-License-Identifier: MIT + +: <<'POD2MAN' +=head1 NAME + +debvm-run - Run a VM image created by debvm-create + +=head1 SYNOPSIS + +B [B<-g>] [B<-i> F] [B<-s> I] [B<--> I] + +=head1 DESCRIPTION + +B is essentially a thin wrapper around B for running a virtual machine image created by B or something compatible. +The virtual machine image is expected to be a raw ext4 image with file system label B. +The architecture of the machine is detected from the contained F. +It must contain a symbolic link pointing to a kernel image at F or F depending on the architecture and a symbolic link pointing to an initrd image at F. +Both are extracted and passed to B. +A net interface configured for user mode is added automatically. + +=head1 OPTIONS + +=over 8 + +=item B<-g>, B<--graphical> + +By default, the option B<-nographic> is passed to B and one interacts with the serial console of the machine. +This configuration is skipped in the presence of this option. + +=item B<-i> F, B<--image>=F + +This option specifies the location of the virtual machine image file. +By default F in the working directory is used. + +=item B<-s> I, B<--sshport>=I + +If given, B is configured to pass connections to I<127.0.0.1:sshport> to port 22 of the virtual machine. +You can connect to your virtual machine without updating your known hosts like this: + + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $sshport root@127.0.0.1 + +=item B<--> I + +All options beyond a double dash are passed to B. +This can be used to configure additional hardware components. +One possible use of this method is passing B<-snapshot> to avoid modifying the virtual machine image. + +=back + +=head1 EXAMPLES + +Run a virtual machine stored in the image F (the default) with +local port 8022 routed to port 22 of the virtual machine. The B<-snapshot> +argument is passed to QEMU and prevents any permanent changes to +F, resulting in an ephemeral run. + + debvm-run -s 8022 -i rootfs.ext4 -- -snapshot + +=head1 LIMITATIONS + +Due to the way kernel and bootloader are being extracted before running B, one cannot upgrade a kernel and then just reboot. +Attempting to do so, will still use the old kernel. +Instead, B must be terminated and B should be launched again to pick up the new kernel. +In order to avoid accidental reboots, one may pass B<-no-reboot> to B. + +=head1 SEE ALSO + + debvm-create(1) qemu(1) + +=cut +POD2MAN + +set -u + +IMAGE=rootfs.ext4 +SSHPORT= +GRAPHICAL= + +nth_arg() { + shift "$1" + printf "%s" "$1" +} + +die() { + echo "$*" 1>&2 + exit 1 +} +usage() { + die "usage: $0 [-g] [-i image] [-s sshport] [-- qemu options]" +} +usage_error() { + echo "error: $*" 1>&2 + usage +} + +opt_graphical() { + GRAPHICAL=1 +} +opt_image() { + IMAGE=$1 +} +opt_sshport() { + SSHPORT=$1 +} + +while getopts :gi:s:-: OPTCHAR; do + case "$OPTCHAR" in + g) opt_graphical ;; + i) opt_image "$OPTARG" ;; + s) opt_sshport "$OPTARG" ;; + -) + case "$OPTARG" in + help) + usage + ;; + graphical|image|sshport) + test "$OPTIND" -gt "$#" && usage_error "missing argument for --$OPTARG" + "opt_$OPTARG" "$(nth_arg "$OPTIND" "$@")" + OPTIND=$((OPTIND+1)) + ;; + image=*|sshport=*) + "opt_${OPTARG%%=*}" "${OPTARG#*=}" + ;; + *) + usage_error "unrecognized option --$OPTARG" + ;; + esac + ;; + :) + usage_error "missing argument for -$OPTARG" + ;; + '?') + usage_erro "unrecognized option -$OPTARG" + ;; + *) + die "internal error while parsing command options, please report a bug" + ;; + esac +done +shift "$((OPTIND - 1))" + +test -f "$IMAGE" || die "image '$IMAGE' not found" +test -s "$IMAGE" || die "image '$IMAGE' is empty" + +if ! printf '\123\357' | cmp --bytes=2 "$IMAGE" - 1080; then + die "image '$IMAGE' is not in ext4 format" +fi + +if ! printf 'debvm\000' | cmp --bytes=6 "$IMAGE" - 1144; then + die "image '$IMAGE' was not created by debvm-create (wrong disk label)" +fi + +cleanup() { + set +x + test -n "$KERNELTMP" && rm -f "$KERNELTMP" + test -n "$INITRDTMP" && rm -f "$INITRDTMP" +} + +trap cleanup EXIT INT TERM QUIT + +KERNELTMP=$(mktemp) +INITRDTMP=$(mktemp) + +ARCHITECTURE=$(dpkg --print-architecture) +VMARCH=$ARCHITECTURE +if command -v elf-arch >/dev/null 2>&1; then + /sbin/debugfs "$IMAGE" -R "cat /bin/true" > "$KERNELTMP" + VMARCH=$(elf-arch "$KERNELTMP") +fi +case "$VMARCH" in + mips*|ppc64el) + KERNELLINK=vmlinux + ;; + *) + KERNELLINK=vmlinuz + ;; +esac + +KERNELNAME=$(/sbin/debugfs "$IMAGE" -R "stat $KERNELLINK" | sed 's/Fast link dest: "\(.*\)"/\1/;t;d') +INITRDNAME=$(/sbin/debugfs "$IMAGE" -R "stat initrd.img" | sed 's/Fast link dest: "\(.*\)"/\1/;t;d') + +test -n "$KERNELNAME" || die "failed to discover kernel image" +test -n "$INITRDNAME" || die "failed to discover initrd image" + +KERNEL_CMDLINE="root=LABEL=debvm rw" +NETDEV="user,id=net0" + +set -- \ + -no-user-config \ + -name "debvm-run $IMAGE" \ + -m 1G \ + -kernel "$KERNELTMP" \ + -initrd "$INITRDTMP" \ + -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0 \ + -drive "media=disk,format=raw,discard=unmap,file=$IMAGE,if=virtio,cache=unsafe" \ + -device "virtio-net-pci,netdev=net0" \ + "$@" + +MAX_SMP= +if test "$ARCHITECTURE" = "$VMARCH"; then + QEMU=kvm + # While kvm will fall back gracefully, the following options can only + # be passed when kvm really is available. + if test -w /dev/kvm; then + set -- -enable-kvm -cpu host "$@" + fi + case "$VMARCH" in + arm64) + set -- -machine type=virt,gic-version=host "$@" + ;; + esac +else + QEMU="qemu-system-$VMARCH" + case "$VMARCH" in + arm64) + QEMU=qemu-system-aarch64 + set -- -machine virt -cpu max "$@" + ;; + arm|armel|armhf) + QEMU=qemu-system-arm + set -- -machine virt -cpu max "$@" + ;; + ppc64el) + QEMU=qemu-system-ppc64 + ;; + mips64el) + MAX_SMP=1 + set -- -cpu 5KEc "$@" + ;; + mipsel) + MAX_SMP=1 + ;; + riscv64) + set -- -machine virt "$@" + ;; + esac +fi +if test -z "$MAX_SMP" || test "$MAX_SMP" -gt 1; then + NPROC=$(nproc) + test -n "$MAX_SMP" && test "$NPROC" -gt "$MAX_SMP" && NPROC=$MAX_SMP + set -- -smp "$NPROC" "$@" +fi + +if test -z "$GRAPHICAL"; then + set -- -nographic "$@" + case "$VMARCH" in + amd64|i386) + KERNEL_CMDLINE="$KERNEL_CMDLINE console=ttyS0" + ;; + esac +fi + +if test -n "$SSHPORT"; then + NETDEV="$NETDEV,hostfwd=tcp:127.0.0.1:$SSHPORT-:22" +fi +DNSSEARCH=$(dnsdomainname) +if test -n "$DNSSEARCH"; then + NETDEV="$NETDEV,domainname=$DNSSEARCH" +fi +set -- \ + -append "$KERNEL_CMDLINE" \ + -netdev "$NETDEV" \ + "$@" + +set -ex + +/sbin/debugfs "$IMAGE" -R "cat $KERNELNAME" > "$KERNELTMP" +/sbin/debugfs "$IMAGE" -R "cat $INITRDNAME" > "$INITRDTMP" + +"$QEMU" "$@" -- cgit v1.2.3 From 52202bfc705e433a170e78529558556b0976ae71 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Fri, 13 Jan 2023 07:04:28 +0100 Subject: automatically set up agetty TERM when possible * debvm-create will now parse a new kernel cmdline debvm.term and if present will pass its value to agetty as TERM. * debvm-run will now detect whether it is running in a terminal in non-graphic mode and pass its environment TERM variable as debvm.term to the kernel cmdline thus closing the loop. --- bin/debvm-run | 3 +++ share/customize-autologin.sh | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'bin/debvm-run') diff --git a/bin/debvm-run b/bin/debvm-run index c2a3f21..4f7ff44 100755 --- a/bin/debvm-run +++ b/bin/debvm-run @@ -250,6 +250,9 @@ if test -z "$GRAPHICAL"; then KERNEL_CMDLINE="$KERNEL_CMDLINE console=ttyS0" ;; esac + if test -t 0 && test -t 1 && test -n "$TERM"; then + KERNEL_CMDLINE="$KERNEL_CMDLINE debvm.term=$TERM" + fi fi if test -n "$SSHPORT"; then diff --git a/share/customize-autologin.sh b/share/customize-autologin.sh index 5592a03..d7df555 100755 --- a/share/customize-autologin.sh +++ b/share/customize-autologin.sh @@ -3,7 +3,8 @@ # SPDX-License-Identifier: MIT # # This is a mmdebstrap customize hook that configures automatic root login on a -# serial console. +# serial console. It also parses the debvm.term kernel cmdline and passes it as +# TERM to agetty. set -eu @@ -15,6 +16,8 @@ mkdir "$TARGET/etc/systemd/system/$UNIT.d" ( echo '[Service]' + printf '%s\n' 'ExecStartPre=/bin/sed -n -e "s/.*\\(^\\| \\)debvm\\.term=\\([^ ]*\\).*/TERM=\\2/w/run/debvmterm" /proc/cmdline' + echo 'EnvironmentFile=-/run/debvmterm' echo 'ExecStart=' sed -n 's,^ExecStart=-/sbin/agetty ,&-a root ,p' "$TARGET/lib/systemd/system/$UNIT" ) > "$TARGET/etc/systemd/system/$UNIT.d/autologin.conf" -- cgit v1.2.3 From ca2cbf6d84dcc96845fcc6521aebe3b5076cdb32 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Fri, 13 Jan 2023 12:55:10 +0100 Subject: change cmdline variable debvm.term to TERM Jochen Sprickerhof found that systemd interprets the TERM variable according to our intended meaning and applies it to pid 1. It doesn't apply it to serial-getty, though so we keep that code for now. --- bin/debvm-run | 2 +- share/customize-autologin.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'bin/debvm-run') diff --git a/bin/debvm-run b/bin/debvm-run index 839653c..6ed47c4 100755 --- a/bin/debvm-run +++ b/bin/debvm-run @@ -268,7 +268,7 @@ if test -z "$GRAPHICAL"; then ;; esac if test -t 0 && test -t 1 && test -n "$TERM"; then - KERNEL_CMDLINE="$KERNEL_CMDLINE debvm.term=$TERM" + KERNEL_CMDLINE="$KERNEL_CMDLINE TERM=$TERM" fi fi diff --git a/share/customize-autologin.sh b/share/customize-autologin.sh index d7df555..b9a4a8b 100755 --- a/share/customize-autologin.sh +++ b/share/customize-autologin.sh @@ -16,7 +16,7 @@ mkdir "$TARGET/etc/systemd/system/$UNIT.d" ( echo '[Service]' - printf '%s\n' 'ExecStartPre=/bin/sed -n -e "s/.*\\(^\\| \\)debvm\\.term=\\([^ ]*\\).*/TERM=\\2/w/run/debvmterm" /proc/cmdline' + printf '%s\n' 'ExecStartPre=/bin/sed -n -e "s/^\\(.* \\)\\?\\(TERM=[^ ]*\\).*/\\2/w/run/debvmterm" /proc/cmdline' echo 'EnvironmentFile=-/run/debvmterm' echo 'ExecStart=' sed -n 's,^ExecStart=-/sbin/agetty ,&-a root ,p' "$TARGET/lib/systemd/system/$UNIT" -- cgit v1.2.3