QEMU services with socat

QEMU Services?

I’ve been working on an integration testing environment for a few interconnected services. Such a thing would be easy with containers, but I am modeling an environment of bare metal hosts running these services, leaving little choice but VMs. It’s been easy testing the VMs in isolation using QEMU, which by default with -nographic drops one into the console.

But when one wants to kick off several VMs at once, it’s much easier if they can be started as services in the background. Any number of service supervisiors can run them, that’s no problem. QEMU even has the -daemonize option to background itself.

But now, with some backgrounded VM service, how can one connect to it and do things? No longer can we use the default console connection from -nographic.

As I was trying to answer this question, I realized my recent experience with socat might lend a hand…

What does -nographic do anyway?

It is a lot easier to know what way to go when you know where you started. So, before we go much further, it is probably good to understand what -nographic does.

From what I could tell, the -nographic option does three things:

  • Sets -display none to disable video output to the user.
  • Sets -echr 0x01 to make ctrl+a the escape sequence.
  • Binds the monitor and serial connections to stdio like -serial mon:stdio.

Well, at least the first of these is pretty obvious: we’re trying to run headless services, so we want -display none.

To understand the escape sequence, we also have to understand what that -serial option is. And what is the monitor?

The manpage has all of this in detail, but it breaks down like this:

  • The serial connection is what gives us console access.
  • The “monitor” interface provides a way to control QEMU and the emulated hardware running the VM after it is has started.
  • The -serial mon:stdio is multiplexing the serial and monitor connections together onto a single file descriptor, in this case that being stdio. To access the multiplex controls, we need an escape sequence, set by the -echr options, which is ctrl+a by default.
  • To get to the monitor connection from a multiplexed serial connection, one can use the escape sequence ctrl+a then press c.

It is important to note that the serial and monitor connections do not have to be multiplexed, but can be bound to two different file descriptors. In fact one can configure multiple serial connections, if needed (though we only get one monitor). As these connection are bound to a file descriptor, we could bind these connections to named files, and then we get unix sockets we can use with other processes. And socat can connect to unix sockets!

How I ended up configuring QEMU

To give the VM something that it can read from/write to for the serial and monitor connections, we actually end up emulating char devices. The -serial mon:stdio is shorthand for something like this:

-chardev stdio,id=char0,signal=off \
-serial mon:char0

Knowing that, we can put together the options in the manpage regarding unix sockets, to get something like this:

-chardev socket,id=char0,path=./monitor.sock,server,nowait \
-mon chardev=char0 \
-chardev socket,id=char1,path=./serial.sock,server,nowait \
-serial chardev:char1

If we add these options to our QEMU command, we’ll end up with two files created in the current directory, both being unix sockets that we can connect to with socat.

The socat command

socat -,icanon=0,echo=0,isig=0,escape=0x0f UNIX-CONNECT:$SOCKET

That’s all there is to it, just sub $SOCKET with the name of the unix socket you want to connect to (serial.sock or monitor.sock in the QEMU example above). Then stdio will be connected to it. As in my previous post, this socat command looks and feels just like a native terminal connection. The only exception is the escape sequence here is ctrl+o, as I like to use ctrl+a to get to the beginning of the line.

Regarding the options we set on the stdio side, we use the icanon option to disable canonical input mode, so we don’t have to wait until we hit enter to pass the input along to the socket side, same as how a serial connection typically works. We also disable socat’s echo, because the shell’s echo on the other end of the connection should take care of that for us. isig we disable as well, as we don’t want socat to handle signals but to send them along too.

There you have it, socat for the win yet again. Truly, the more I understand what this tool can do, the more I see simple but powerful uses lying all around.

Need a good way to level up you unix powers? Learn socat.