Enabling libvirt channels by adding virtio_console to the openSUSE installation

libvirt supports the concept of so-called channels that represent private communication channels between a libvirt guest and its host.

Like a serial port, they appear to the guest through device nodes, however while the former appear as /dev/ttyS*, channels appear as /dev/virto-ports/name where name is a user-configured name. Where serial ports may be probed by the guest operating system when checking for connected devices, channels are likely to be ignored. Where serial ports are often used for console purposes (eg. to provide serial console access by running a getty on the serial port), channels are eg. used by the SPICE guest agent to implement features such as clipboard sharing. Fedora’s Anaconda installer can use them to forward guest logs to the host.

openSUSE‘s YaST installer doesn’t, however, and even worse: its installation system, a special initramfs located on the installation media, misses the virtio_console kernel module necessary to make the device nodes to appear in the guest system. It is part of the kernel installed later on, though.

Luckily, this can be fixed without modifying the original installation media. openSUSE provides a mechanism called “Driver Update Disk” (DUD) which can be used to augment the installation system with the module. The following Bash code will take a mounted openSUSE ISO, extract the module from its kernel-default RPM (which is the same kernel that is active during installation) and create a DUD containing it:

echo -n " * Detecting openSUSE version: "
OSVERSION=$(sed -n '/^product/{s/.* //;p}' $ISO_MOUNTED/boot/x86_64/loader/gfxboot.cfg)
if [ -z "$OSVERSION" ] ; then
  echo "failed!"
  exit 1
else
  echo ${OSVERSION}
fi

echo "* Creating Driver Update Disk (DUD) image containing virtio_console"

MODEXTRACT_TMPDIR=$(mktemp -d ${VM}_modextract.XXXXXX)
DUD_TMPDIR=$(mktemp -d ${VM}_dud.XXXXXX)

echo " * Creating DUD directory structure..."
mkdir -p $DUD_TMPDIR/linux/suse/x86_64-$OSVERSION/modules

echo " * Creating dud.config..."
echo  >$DUD_TMPDIR/dud.config "UpdateName: virtio_console module from openSUSE $OSVERSION default kernel"
echo >>$DUD_TMPDIR/dud.config "UpdateID: e82ffff3-34d9-4f6b-bd58-b638f234776"

echo " * Extracting virto_console.ko..."
cd $MODEXTRACT_TMPDIR
rpm2cpio $ISO_MOUNTED/suse/x86_64/kernel-default-[0123456789]*.rpm | cpio --quiet -id
find \
 -name virtio_console.ko \
 -exec mv {} ../$DUD_TMPDIR/linux/suse/x86_64-$OSVERSION/modules/ \;
cd ..

echo " * Generating DUD archive..."
(cd $DUD_TMPDIR && find | cpio --quiet -o >../virtio_console_$OSVERSION.cpio)
gzip virtio_console_$OSVERSION.cpio

echo " * Removing tempdirs..."
rm -r $MODEXTRACT_TMPDIR
rm -r $DUD_TMPDIR

You can then use virt-install‘s initrd-inject feature to make the DUD image become part of the initramfs and extra-args to inform YaST about it:

virt-install \
[...]
  --extra-args="driverupdate=file:///virtio_console_$OSVERSION.cpio.gz"
  --initrd-inject=virtio_console_$OSVERSION.cpio.gz

Then adding a channel option such as this:

  --channel tcp,host=127.0.0.1:12345,mode=bind,target_type=virtio,name=foo.org.channel

will make /dev/virtio-parts/foo.org.channel available in the guest and anything written to it can be accessed from the host by using a command such as netcat 127.0.0.1 12345.

Note that using tcp or udp, such as in this example, avoids permission problems: if you use eg. file instead, that file will be owned by qemu or libvirt respectively and generally not easily be accessible without root rights or further configuration to grant your user access.

Leave a comment