Christian Hornung's Software Development Pages


Menu

On-demand virtual machine (VM) with VirtualBox and xinetd

In case you ever have the need to run a virtual machine (VM) that is remote controlled via (V)RDP on a linux host in an on-demand fashion, i.e. it is started automatically when a RDP connection is opened, here is a way to do it.

VBoxHeadless - The VirtualBox VRDP server without GUI

With the VirtualBox installation comes VBoxHeadless, a daemon that runs a VM and provides the VRDP service to control it without opening any GUI on the host system. We will use it to run our on-demand VM.

VBoxHeadless takes (among others) the name of the VM to start and optionally the TCP port number to listen on as command line arguments. So the following simple command could be used to start the VM, listening on the default port 3389:

/usr/bin/VBoxHeadless -s VMNAME

xinetd

The first solution to the on-demand problem that came to mind was xinetd, a super-server that listens for incoming connections and can spawn child processes to handle them as they arrive.

So my first try was to simply set up a xinetd service on port 3389 that runs VBoxHeadless as specified above. This failed miserably because you cannot just run any server under xinetd. The server process must be designed for that.

Basically there are two possible modes of operation for TCP servers under xinetd:

  • "wait=no":
    This means that xinetd starts a new child process for each incoming connection and does not wait for termination of the child. In this mode, the TCP connection is accepted by xinetd and redirected to stdin and stdout of the child. So the child must use stdin and stdout for communication instead of opening its own socket.
  • "wait=yes":
    Xinetd only starts one child process on the first incoming connection and then waits for its termination until doing that again. In this mode the child accepts the TCP connection (and possibly further incoming connections), but the child must not try to listen on the port because that fails as xinetd is already listening.

Obviously both modes cannot work with VBoxHeadless directly.

A little shell script to the rescue

So I created a shell script that acts as a xinetd-compatible wrapper around VBoxHeadless. Here is the code:

#!/bin/sh

VMSTATE=`/usr/bin/VBoxManage showvminfo VMNAME --machinereadable | grep VMState\=`

if [ $VMSTATE = "VMState=\"poweroff\"" ]; then
        rm -rf /tmp/vbox-xinetd-log &> /dev/null
        /usr/bin/VBoxHeadless -s VMNAME -p 53389 &> /tmp/vbox-xinetd-log &
        sleep 10
fi

exec nc -T lowdelay 127.0.0.1 53389

This script is intended to be run from xinetd and first checks the state of the VM with the VBoxManage command. If the VM is powered off, it starts VBoxHeadless in the background, but not on the default port. Instead, VBoxHeadless should listen on another unused port, I used 53389 here.

Then we have to wait until VBoxHeadless is ready to accept a connection. I know that sleep 10 is ugly, but I already tried to improve that by reading from the temporary log file of VBoxHeadless and failed. That file always reads as empty until the VBoxHeadless process terminates, so it seems like its output is never flushed.

After having made sure that the VM is running, we have to forward the incoming connection to the VBoxHeadless process. Here I am running the script in "wait=no" mode and so I can use the tool netcat (nc) to connect stdin and stdout of the running script to the started VBoxHeadless instance.

Here is the xinetd config I used for this service (this goes somewhere in /etc/xinetd.conf or /etc/xinetd.d/... depending on your installation):

# default: off
# description: Runs the virtual machine when an RDP connection is opened.
service ms-wbt-server
{
        disable = no
        socket_type     = stream
        protocol        = tcp
        port            = 3389
        wait            = no
        user            = christian
        group           = vboxusers
        server          = /usr/bin/VBox-xinetd
        log_on_success  += EXIT
        instances       = 1
}

The path in the "server" line must point to the script created above.
And "ms-wbt-server" must be defined as TCP port 3389 in /etc/services, this entry was already there on my system.

And there you go:

Microsoft Windows 2000 starting on virtual machine


Modified: 2009-07-11 Copyright © 2006-2010 Christian Hornung - chhornung@googlemail.com