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…
-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:
-display none
to disable video output to the user.-echr 0x01
to make ctrl+a the escape sequence.-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:
-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.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!
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.
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.