2

In my particular case, with a new server installation of Ubuntu 18.04 Bionic and VirtualBox-5.2.20, it appears the old method of auto-starting guests is no longer available. Not that it was that wonderful - but it functioned. There doesn't appear to be any clean way to go about this - how can systemd and VirtualBox work together for intelligent booting, control, and shutdown?

2 Answers 2

5

Similar to your solution, but a bit simpler:

  1. Run systemctl edit vbox@.service --full --force and paste the following content in, updating User and Group to your username.
[Unit]
Description=Virtual Box Guest %I
After=network.target vboxdrv.service
Before=runlevel2.target shutdown.target
 
[Service]
User=USERNAME
Group=GROUPNAME
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
 
ExecStart=/usr/bin/VBoxManage startvm %i --type headless
ExecStop=/usr/bin/VBoxManage controlvm %i acpipowerbutton
 
[Install]
WantedBy=multi-user.target
  1. Reload systemd: systemctl daemon-reload

  2. Get a list of your VM's VBoxManage list vms:

$ VBoxManage list vms
"Ubuntu" {1ba32309-d4c4-420a-a9c8-a38177f00bc4}
"Windows" {573df054-0e33-4389-896a-1234f10e25ad}
  1. Use the name returned in step 3 to manage the VM via systemd. For example, to manage the "Ubuntu" VM you would run:
sudo systemctl start vbox@Ubuntu     # Start the VM
sudo systemctl enable vbox@Ubuntu    # Start the VM on boot
1

Well...I think I came up with an answer.

This is my solution. It's certainly not perfect - and part of the reason I'm sharing is to invite improvement. However - this provides for autostart, monitoring, and shutdown via systemd. And I think this is at least going in the right direction for these two products.

One caveat - the below presumes a little familiarity with both systemd & VirtualBox. It also requires ACPI shutdown functions enabled on the guests. That may be built-in to Windows, and as simple as installing acpid on Linux, but I'm not assuming. Also some guests (Windows version xxxx) may need some "tweaking" to ensure ACPI shutdowns happen immediately - I found a wonderful resource at https://ethertubes.com/unattended-acpi-shutdown-of-windows-server/

First - need to create a systemd unit of course. I'm leveraging the availability of templates.

executing systemctl edit --full vbox@.service provides an editor into which we place: [Unit] Description=VirtualBox %I Virtual Server After=network.target vboxdrv.service

[Service]
Type=forking
Restart=no
TimeoutSec=5min
KillMode=process
RuntimeDirectory=vbox
RuntimeDirectoryPreserve=yes
PIDFile=/run/vbox/%I.pid

Environment='RUNDIR=/run'
Environment='PIDDIR=/vbox'
Environment='VM=%I'
ExecStart=/etc/init.d/vbox-systemd start
ExecStop=/etc/init.d/vbox-systemd stop

[Install]
WantedBy=multi-user.target

The above provides a basis:

- Allows for up to 5 minutes for startup/shutdown per guest
- The pid files will be stored as /run/vbox/<guest>.pid
- And the guests will be started as part of the normal boot process

This can be adjusted to taste - but apart from server-wide defaults leave this alone. Individual guests will be tailored later. Now - need to provide the helper script. I just spent an extended period of time fighting with the intricacies of BASH, a lack of sleep, and a severe lack of active experience with BASH. So the following works, there's a mix of styles, and I really wish I knew why my attempt at simple BASH functions failed so miserably. But I'm ready for bed so while it's not my best work it...it does work!:

#! /bin/bash
# /etc/init.d/vbox-systemd: Helper script to startup & shutdown VirtualBox
# headless machines via systemd
#
# written by Daniel L. Miller <dmiller@amfes.com>

# This should not be called directly (though possible with the
# proper environment variables set). This is used by the
# vbox@.service template to start & stop virtual machines - 
# with supervision.

# Environment variables to be defined for us by systemd unit
# RUNDIR=/run
# PIDDIR=/vbox
# VM=<vmname>

# This was setup to use environment variables - maybe support cmd line as well.
if [ ! -z "$2" ]; then
    VM=$2
fi

# So...I suppose might as well set sane defaults
if [ -z "$RUNDIR" ]; then
    RUNDIR='/run'
fi
if [ -z "$PIDDIR" ]; then
    PIDDIR='/vbox'
fi

#
# Overprotective but trying to be good...
# These utilities should be fairly standard...
#
VB=/usr/bin/VBoxManage
GREP=/bin/grep
CUT=/usr/bin/cut
TR=/usr/bin/tr
SLEEP=/bin/sleep
WAITEXIT=300

# Make sure the utilities are available
test -x $VB || exit 3
test -x $GREP || exit 3
test -x $CUT || exit 3
test -x $TR || exit 3

# Verify the pid folder tree is defined and usable
test -d "${RUNDIR:?run directory top-level must be set}" || exit 3
test -d "$RUNDIR${PIDDIR:?pid directory must be set}" || mkdir -p "$RUNDIR$PIDDIR"
# This test is a little different - this validates the name but we don't
# care if the file exists or not. At least the moment.
test -f "$RUNDIR$PIDDIR/${VM:?Virtual Machine name must be set}.pid"

PIDFILE=$RUNDIR$PIDDIR/$VM.pid

vmactive=$($VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"')

case "${1:-''}" in
  'start')
    # Start the machine
    $VB startvm $VM --type headless
    # Give it at least a change to get started...
    $SLEEP 2
    # Now perform first trick and save pid
    vmactive=`$VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"'`
    if [ "x$vmactive" == "x$VM" ]; then
        vmpid=$($VB showvminfo $VM --log 0 | $GREP -m 1 'Process ID' | $CUT -d ':' -f4 | $TR -d ' ')
        echo $vmpid > $PIDFILE
    else
        exit 1;
    fi
    ;;
  'stop')
    waited=0
    while [ "$waited" -lt $WAITEXIT ]; do
        # Test first so VB doesn't object to shutting off a non-running VM
        vmactive=`$VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"'`
        if [ "x$vmactive" != "x$VM" ]; then
            echo "Proper ACPI Shutdown of $VM - or it wasn't running!"
            break
        fi
        # Try to turn it off - repeatedly
        $VB controlvm $VM acpipowerbutton
        # Give it a chance to take.
        $SLEEP 5
        waited=$((waited+5))
    done

    # Time to clean up - force terminate if necessary and delete the pid file
    [ "$waited" -ge $WAITEXIT ] && [ -f $PIDFILE ] && kill -s 9 $PIDFILE
    [ -f $PIDFILE ] && rm $PIDFILE
    ;;
  'status')
    vmactive=`$VB list runningvms | grep $VM | cut -d ' ' -f 1 | tr -d '"'`
    if [ "x$vmactive" == "x$VM" ]; then
        vmpid=$($VB showvminfo $VM --log 0 | $GREP -m 1 'Process ID' | $CUT -d ':' -f4 | $TR -d ' ')
        echo "$VM is running as PID $vmpid"
    else
        echo "$VM is not running"
    fi
    ;;
  *)
    echo "Usage: vbox-systemd [start|stop|status]" >&2
    exit 3;
    ;;
esac

So...now all that's necessary to start a virtual machine is: systemctl start vbox@<your-guest-name>. More exciting - systemctl status vbox@<your-guest-name> will provide systemd status of the VM! And you can even do a systemctl stop vbox@<your-guest-name> to turn it off.

To activate auto-start - simply run systemctl enable vbox@<your-guest-name>.

Now - if you need extra control, like specifying the order in which the guests boot, use the tricky systemctl edit vbox@<your-guest-name>. Notice this time we're not using the --full argument - this creates an override folder just for this guest without duplicating the base unit. Maybe this guest needs SQL services from the host:

[Unit]
After=mysql.service
Wants=mysql.service

Now this guest won't be started until the mysql server is up. Or if this guest provides critical services you can add:

[Services]
Restart=yes

Remember - just put in the systemd arguments you need to add or override - the rest come from the template.

I hope this helps someone else - and if others can contribute please do!

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.