6

Virtualbox has the ability to issue a command to a running vm:

vboxmanage controlvm NameOfRunningVM acpipowerbutton

However this command returns immediately which results in non-graceful shutdown for my situation.

The situation: I plan on using this in an /etc/init.d script. This would allow for graceful shutdown of all the running VMs. Currently when I issue the vboxmanage controlvm NameOfRunningVM acpipowerbutton command the shutdown gets cutoff because the command doesn't wait for the VM to shutdown.

I need a Bash script that takes as input the name of the Virtualbox machine and a timeout in seconds then waits for the VM to return to "poweroff" state or the timeout occurs?

I'm not sure what is the best way to go about doing this.


I was thinking of checking the state of the VM with the following command:

[user@machine ~]$ vboxmanage list runningvms
"VirtualMachineName" {65c93f1f-4508-4119-b07d-ce9e89b23b8e}

The bash script would maybe be polling for a list of running VMs. Once the machine name stops being listed, the VM would be considered finished.

2 Answers2

16

Using polling, it could be done like this:

#!/bin/bash
MACHINE=$1
echo "Waiting for machine $MACHINE to poweroff..."

until $(VBoxManage showvminfo --machinereadable $MACHINE | grep -q ^VMState=.poweroff.)
do
  sleep 1
done
larstobi
  • 311
  • 1
  • 5
2

same idea as @larstobi, but less fuss by offloading poll to watch command:

$ vboxmanage list vms
"guest" {fubar-rabuf-bufu}
$ vboxmanage showvminfo guest | grep -i state
State:           running (since 2020-08-05T02:37:13.784000000)
$ printf '( watch -te  -- "! vboxmanage showvminfo guest | grep -i poweroff" >&- & wait )' | time -p bash
Command terminated by signal 2
real 5.05
user 0.00
sys 0.00

The -e flag causes exit on non-zero result of command, while -t turns off annoying headers; we close stdout for watch, since the purpose is to block and we run in fork, while technically blocking with wait which is interruptible.

I am passing the command string to time -p bash, so that I can easily attach time for the purposes of this demonstration, but in practice, you could just do:

$ ( watch -te  -- "! expression" >&- & wait )

Disclaimer: none of this is really tested