I would recommend a more sophisticated approach including an upstart job, a start and stop script. As example I am using Windows XP, as my home directory lets use tombert ... which you should change accordingly. It has the advantage of whatever you do (reboot, shutdown, pressing the power button) it handles your virtual machine nicely.
First the upstart job, put into /etc/init/winxpvm.conf:
description "WinXP VirtualBox job"
author "Thomas Perschak"
## 0: system halt
## 1: single-user mode
## 2: graphical multi-user plus networking
## 6: system reboot
start on started rc RUNLEVEL=[2]
stop on starting rc RUNLEVEL=[!2]
## upstart config
kill timeout 120
kill signal SIGCONT
nice -10
## start WinXP VirtualBox
exec /home/tombert/scripts/winxpvm-start.sh
## stop WinXP VirtualBox
pre-stop exec /home/tombert/scripts/winxpvm-stop.sh
The upstart job starts the virtual machine in runlevel 2 (which is in graphical mode), and in my case it increases the priority with nice
. In order to nicely shutdown the virtual machine I need to "disable" the upstart termination using the kill signal SIGCONT
statement. This leaves the virtual machine running at first (avoiding the default SIGTERM
). After 120 seconds the SIGKILL
is send anyhow. Instead I am running the winxpvm-stop.sh
script.
Side-Note 1: The stanzas start on started runlevel [2]
and stop on starting runlevel [!2]
do not work. One has to specifically mention the job rc
.
Side-Note 2: What is confusing also from the upstart manual: The kill signal
stanza specifies the signal sent after 5 seconds. In this example I set it from SIGTERM
(default) to SIGCONT - but the 5 seconds timeout I was unable to change. The kill timeout
stanza specifies the timeout after which the SIGKILL
is sent - which signal one cannot change. An improvement therefore would be to define new stanzas term signal
and term timeout
.
Here the start script winxpvm-start.sh:
#! /bin/bash -e
function dostart()
{
echo -n "Running WinXP ... "
vboxheadless --startvm WinXP
echo "now closed"
}
export -f dostart
if [ $(whoami) != "tombert" ]; then
su -c dostart tombert
else
dostart
fi
Since all the settings etc. are done in user mode (as my login is tombert), even when run as root I change the account to tombert. The user of course could be changed in the upstart configuration but this solution leaves me the option to start/stop the virtual machine "by hand" from the console.
The more interesting is the shutdown script in winxpvm-stop.sh:
#! /bin/bash
function dostop()
{
## check if WinXP is running
vboxmanage showvminfo WinXP --machinereadable | grep -q 'VMState="running"' &> /dev/null
if [ $? -ne 0 ]; then
echo "WinXP not running"
exit
fi
## try gracefully shutdown
echo -n "Shutting down WinXP ... "
#vboxmanage controlvm WinXP acpipowerbutton
vboxmanage guestcontrol WinXP execute --image "%SystemRoot%\system32\shutdown.exe" --username tombert --password <mypassword> --wait-exit -- "-s" "-f" "-t" "0" &> /dev/null
## check vm status
INDEX=60
while [ $INDEX -gt 0 ]; do
echo -n "$INDEX "
vboxmanage showvminfo WinXP --machinereadable | grep -q 'VMState="running"' &> /dev/null
if [ $? -ne 0 ]; then
echo "gracefully done"
break
fi
sleep 1
let INDEX+=-1
done
## close forcefully
if [ $INDEX -eq 0 ]; then
vboxmanage controlvm WinXP poweroff &> /dev/null
echo "forcefully done"
fi
}
export -f dostop
if [ $(whoami) != "tombert" ]; then
su -c dostop tombert
else
dostop
fi
First I do the same as in the start script - I am changing the user from root to my account tombert. Now lets look at the function dostop
. First I am checking if the virtual machine is even running. Then I am trying to "softly" shutdown by sending a shutdown directly to WinXP using guestcontrol
. Here you must provide the credentials for the WinXP account, which in my case is tombert and a password. The Windows shutdown
will gracefully close all applications and power off the operating system (normally). Then lets check the virtual machine state continuously using showvminfo
. Doing this at least 60 times with 1 second timeout (do whatever you think appropiate is here) should leave the virtual machine enough time to shutdown gracefully. Note that the call to showvminfo
also takes a little bit less than a second (at least on my computer) so this gives it ~120 seconds in my case. If everything brakes we can forcefully shutdown using the poweroff
statement.
You also should see the acpipowerbutton
, but unused. This is because it does not work reliable. If you are logged on to Windows, or even worse multiple users, Windows will show a confirmation shutdown dialog preventing the system from shutdown. This is also the reason why the acpibutton
in the /etc/default/virtualbox
will not work 100% reliable. Also the poweroff
will forcefully shutdown the virtual machine - same as a long-press power button. Therefore it is best to set this to empty:
Excerpt from /etc/default/virtualbox:
# SHUTDOWN_USERS="foo bar"
# check for running VMs of user 'foo' and user 'bar'
# 'all' checks for all active users
# SHUTDOWN=poweroff
# SHUTDOWN=acpibutton
# SHUTDOWN=savestate
# select one of these shutdown methods for running VMs
# acpibutton and savestate causes the init script to wait
# 30 seconds for the VMs to shutdown
SHUTDOWN_USERS=""
SHUTDOWN=""
To make it perfect you might want to change the power button behaviour:
Excerpt from /etc/acpi/powerbtn.sh:
#!/bin/sh
# /etc/acpi/powerbtn.sh
# Initiates a shutdown when the power putton has been
# pressed.
# @backup
# plain shutdown
/sbin/shutdown -h now "Power button pressed"
# fini
exit 0
...
...
There is one little drawback left. When the virtual machine is still booting and the guest control service is not up (in the virtual machine) it will not receive the shutdown command. A rare case ... but think about it.
Thats it, hope it helps.