HOWTO - virtualbox as a service on Windows (cygrunsrv.exe)
Posted: 20. Oct 2009, 09:13
Yet another virtual-machine-as-Windows-service howto, this time with Cygwin's cygrunsrv utility.
Tested with:
Tested with:
- VirtualBox 3.0.8
- Windows 7 RC 64 host
- Cygwin 1.7b
- default packages (bash, grep, sed)
- cygrunsrv (wrapper enabling registration and execution of cygwin binaries as Windows services)
- Create and configure your virtual machine.
- Ensure all VirtualBox files (vm, vdi, config) are modifiable by the user you wish the service to run as (e.g. SYSTEM).
- Install Cygwin for all users and include default packages + those mentioned above.
- Save the bash scripts (see the bottom of this post) somewhere handy (e.g. /home/user/bin) and make sure they're executable.
- vboxd-install (helper script for service installation via cygrunsrv)
- vboxd (script executed by installed services)
- .libcommon (shared function definitions)
- Open a cygwin shell with admin rights.
- Run vboxd-install. Arguments are vmName, vmPort (the port on which VRDP access to the vm is exposed), and vmUser (optional; defaults to SYSTEM):
Note that VBOX_USER_HOME must reference the path containing your VirtualBox.xml file. This path is registered during service installation, so if you move your VirtualBox.xml file you'll have to reinstall the service to ensure the correct path is defined. Alternately, hard code this variable within the vboxd script.
Code: Select all
$ VBOX_USER_HOME="/path/to/.VirtualBox/" vboxd-install myvm 3333
- Open a cygwin shell with admin rights.
- List all cygrunsrv services (you should see vboxd-vmName in the output list):
Code: Select all
$ cygrunsrv --list vboxd-myvm
- Query the status of your vboxd-vmName service:
Code: Select all
$ cygrunsrv --query vboxd-myvm Service : vboxd-myvm Description : VirtualBox virtual machine 'myvm' on port 3333 Current State : Stopped Command : /home/bob/bin/vboxd
- Start the vboxd-vmName service manually:
Code: Select all
$ cygrunsrv --start vboxd-myvm
- Look at the log for the vboxd-vmName service:
Code: Select all
$ cat /var/log/vboxd-myvm.log vboxd 2009-10-20 12:12:59 [INFO] Querying virtual machine 'myvm' state vboxd 2009-10-20 12:13:00 [INFO] Virtual machine 'myvm' is powered off (since 2009-10-20T15:53:58.000000000) vboxd 2009-10-20 12:13:01 [INFO] Starting virtual machine 'myvm' on port 3333 vboxd 2009-10-20 12:13:01 [INFO] Waiting on VBoxHeadless child process 1076
- Stop the vboxd-vmName service:
Code: Select all
$ cygrunsrv --stop vbox-myvm
- Look again at the log for the vboxd-vmName service:
Note that console output from VBoxHeadless isn't flushed to the log until the process terminates. This is why we see "Listening on port 3333" at the very end.
Code: Select all
$ tail -n 12 /var/log/vboxd-myvm.log vboxd 2009-10-20 12:13:01 [INFO] Waiting on VBoxHeadless child process 1076 vboxd 2009-10-20 12:18:50 [WARN] Stopping virtual machine 'myvm' VirtualBox Command Line Management Interface Version 3.0.8 (C) 2005-2009 Sun Microsystems, Inc. All rights reserved. 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% VirtualBox Headless Interface 3.0.8 (C) 2008-2009 Sun Microsystems, Inc. All rights reserved. Listening on port 3333
- Check the state of the vmName virtual machine:
Code: Select all
$ VBoxManage showvminfo myvm | grep 'State' State: saved (since 2009-10-20T16:18:51.000000000)
- vboxd-install
Code: Select all
#!/bin/bash ## ## Registers a VirtualBox virtual machine to start as a service via cygrunsrv ## ## load common functions basedir="$(readlink -f $(dirname $0))" source "$basedir/.libcommon" || exit 1 ## test for presence of cygrunsrv utility if [ ! -x "$(which cygrunsrv)" ]; then die "Utility 'cygrunsrv' is not in path" fi ## test VirtualBox configuration if [ -z "$VBOX_USER_HOME" ]; then die "Required environment variable 'VBOX_USER_HOME' is undefined. " \ "Please ensure this variable is set to point to the directory " \ "containing your VirtualBox.xml configuration file." fi configFile=$(cygpath -u "$VBOX_USER_HOME\\VirtualBox.xml") if [ ! -e "$configFile" ]; then die "VirtualBox configuration file '$(cygpath -w $configFile)' not found" fi ## parse arguments parseArg vmName "$1" parseArg vmPort "$2" parseArg vmUser "$3" "SYSTEM" ## if vmUser is not SYSTEM, update userSpec userSpec="--interactive" if [ "$vmUser" != "SYSTEM" ]; then ## "interactive" option disallowed when user is specified userSpec="--user \"$vmUser\"" fi ## install the service cygrunsrv \ --install "vboxd-$vmName" \ --path "$basedir/vboxd" \ --env "VBOXD_VM_NAME=$vmName" \ --env "VBOXD_VM_PORT=$vmPort" \ --env "VBOX_USER_HOME=$VBOX_USER_HOME" \ --desc "VirtualBox virtual machine '$vmName' on port $vmPort" \ $userSpec \ --type auto \ --termsig TERM \ --shutsig TERM \ --neverexits \ --preshutdown \ || die "Failed to install service"
- vboxd
Code: Select all
#!/bin/bash ## ## Manages start / stop of VirtualBox virtual machines ## ## load common functions basedir="$(readlink -f $(dirname $0))" source "$basedir/.libcommon" || exit 1 ## parse arguments parseArg vmName "$1" "$VBOXD_VM_NAME" parseArg vmPort "$2" "$VBOXD_VM_PORT" ## define signal handler function onHalt { warn "Stopping virtual machine '$vmName'" "$VBOX_INSTALL_PATH/VBoxManage" controlvm "$vmName" savestate exit 0 } ## install signal handler; cygrunsrv uses SIGTERM by default trap 'onHalt' TERM ## hardcode this path if you like; it's required for VBox* utils to ## find the correct VirtualBox.xml config file and is usually set ## during a call to vboxd-install. #export VBOX_USER_HOME="$USERPROFILE\\.VirtualBox" ## default VBoxHeadless port specification portSpec="-p $vmPort" ## determine vm state info "Querying virtual machine '$vmName' state" vmState=$( \ "$VBOX_INSTALL_PATH/VBoxManage" showvminfo "$vmName" \ | grep '^State:' \ | sed 's/State: *//' ) info "Virtual machine '$vmName' is $vmState" ## if vm state is saved, we can't specify port without an exception, ## as port spec requires modification of the (immutable) saved machine ## state. See http://www.virtualbox.de/ticket/3609 if [ "${vmState##saved}" != "$vmState" ]; then ## state is saved; clear port specification warn "Port specification is not allowed for saved vms" portSpec="" fi ## start the VM info "Starting virtual machine '$vmName' on port $vmPort" "$VBOX_INSTALL_PATH/VBoxHeadless" -s "$vmName" $portSpec & ## record pid of VBoxHeadless child process and wait on it pid="$!" info "Waiting on VBoxHeadless child process $pid" wait "$pid"
- .libcommon
Code: Select all
# -*-shell-script-*- SCRIPT="$(basename $0)" BASEDIR="$(readlink -f $(dirname $0))" [ -z "$LOGLEVEL" ] && LOGLEVEL=2 [ -z "$LOGDATEFORMAT" ] && LOGDATEFORMAT="%Y-%m-%d %H:%M:%S " function log { local now="" [ -n "$LOGDATEFORMAT" ] && now=$(date +"$LOGDATEFORMAT") echo "$SCRIPT $now$@" >&2 } function debug { [ "$LOGLEVEL" -lt 3 ] && return log "[DEBUG] $@" } function info { [ "$LOGLEVEL" -lt 2 ] && return log "[INFO] $@" } function warn { [ "$LOGLEVEL" -lt 1 ] && return log "[WARN] $@" } function error { log "[ERROR] $@" } function die { error "$@" exit 1 } function parseArg { local _name="$1" local _value="$2" local _default="$3" if [ -z "$_value" ]; then if [ -z "$_default" ]; then die "Required argument '$_name' is undefined" fi if [ "$_default" = "*EMPTY*" ]; then _value="" else _value="$_default" fi fi debug "$_name=\"$_value\"" eval "$_name=\"$_value\"" }