It is ideal of run the guest operating systems that run as servers in headless mode.
The script below does just that. It is a bit buggy but works just fine. It was developed on Ubuntu but I expect it to run suitably on other Debian systems. It can be customized to run on other Linux/ UNIX distros.
1 #!/bin/sh
2 #Author: Emmanuel Toko
3 #/etc/init.d/vbox_mgmt
4 #
5 ### BEGIN INIT INFO
6 # Provides: vm_mgmt
7 # Required-Start: $network $remote_fs $VBoxManage $VBoxHeadless $syslog
8 # Required-Stop: $network $remote_fs $VBoxManage $VBoxHeadless $syslog
9 # Default-Start: 2 3 4 5
10 # Default-Stop: 0 1 6
11 # Short-Description: Start VMs in headless mode.
12 # Description: Run VMs for the default VirtualBox user in
13 # headless mode. Make sure all VMs are using different RDP
14 # ports.
15 ### END INIT INFO
16
17 VBOX_USER=chapati
18
19 # The list of VMs to run. Leave empty to run all registered VMs.
20 VBOX_LIST=""
21
22 # VirtualBox executables
23 VBOX_MANAGE=/usr/bin/VBoxManage
24 VBOX_HEADLESS=/usr/bin/VBoxHeadless
25
26 # Do NOT "set -e"
27
28 # PATH should only include /usr/* if it runs after the mountnfs.sh script
29 PATH=/sbin:/usr/sbin:/bin:/usr/bin
30 DESC="VirtualBox daemon"
31 NAME=vm_mgmt
32 DAEMON=$VBOX_HEADLESS
33 DAEMON_ARGS=""
34 PIDFILE=/var/run/$NAME.pid
35 #SCRIPTNAME=/etc/init.d/$NAME
36 SCRIPTNAME=/etc/init.d/$NAME
37 BATTERY_THRESHOLD=3
38 # Exit if the package is not installed
39 [ -x "$DAEMON" ] || exit 0
40
41 # Read configuration variable file if it is present
42 [ -r /etc/default/$NAME ] && . /etc/default/$NAME
43
44 # Load the VERBOSE setting and other rcS variables
45 . /lib/init/vars.sh
46
47 # LSB log_* functions.
48 . /lib/lsb/init-functions
49
50 vm_init_list()
51 {
52 CHECK_VM_TYPE=0
53 LIST_VMS=""
54 # get registered VMs
55
56 if [ "$CHECK_VM_TYPE" -eq "$1" ]
57 then
58 LIST_VMS=`sudo -H -u $VBOX_USER $VBOX_MANAGE --nologo list vms | cut -d ' ' -f 1 | tr -d '"'`
59 else
60 LIST_VMS=`sudo -H -u $VBOX_USER $VBOX_MANAGE --nologo list runningvms | cut -d ' ' -f 1 | tr -d '"'`
61 fi
62
63 # check for list of VMs
64 if [ -z "$VBOX_LIST" ]
65 then
66 # all registered VMs for user
67 VBOX_LIST=$LIST_VMS
68 else
69 # check that VMs exist
70 for VM in $VBOX_LIST
71 do
72 case $LIST_VMS in
73 "$VM")
74 continue
75 ;;
76 *)
77 log_failure_msg "ERROR: VM '$VM' is not registered!"
78 exit 1
79 ;;
80 esac
81 done
82 fi
83 }
84
85 # get uuid for vm
86 vm_get_uuid()
87 {
88 vm=$1
89 hwuuid=`sudo -H -u $VBOX_USER $VBOX_MANAGE --nologo showvminfo --machinereadable "$vm" | grep 'hardwareuuid='`
90 echo $hwuuid | cut -d '=' -f 2 | tr -d '"'
91 }
92
93 # control running vm
94 vm_ctrl()
95 {
96 sudo -H -u $VBOX_USER $VBOX_MANAGE --nologo controlvm $1 $2 > /dev/null 2>&1
97 }
98
99 #
100 # Function that starts the daemon/service
101 #
102 do_start()
103 {
104 #First do a check on the amount of battery that the system has at this point
105 BATTERY_LEVEL=0
106
107 #acpi outputs battery info like so: Battery 0: Discharging, 93%, 07:07:03 remaining
108 BATTERY_LEVEL=`acpi | awk '{print $4}' | tr -d "%,"`
109
110 if [ "$BATTERY_LEVEL" -le "$BATTERY_THRESHOLD" ]
111 then
112 log_failure_msg ":( The battery level is low [$BATTERY_LEVEL%] so halting...."
113 exit 1
114 fi
115
116 vm_init_list 0
117
118 # Return
119 # 0 if daemon has been started
120 # 1 if daemon was already running
121 # 2 if daemon could not be started
122 RETVAL=0
123
124 VM_EXISTS=0
125 FALSE=0
126 TRUE=1
127
128 #Internal function that will "safely" a VM or VMS. Function takes one..
129 #argument i.e. the VM name and attempts to start that VM
130 start_vm()
131 {
132 VM=$1
133 VM_UUID=`vm_get_uuid $VM`
134 VM_PIDFILE="$PIDFILE.$VM_UUID"
135 VM_DAEMON="$DAEMON"
136 VM_DAEMON_ARGS="$DAEMON_ARGS --startvm $VM_UUID"
137
138 log_action_begin_msg "Starting VM '$1'"
139
140 # test for running VM
141 USER=$VBOX_USER LOGNAME=$VBOX_USER start-stop-daemon \
142 --start \
143 --quiet \
144 --pidfile $VM_PIDFILE \
145 --startas $VM_DAEMON \
146 --test \
147 > /dev/null
148
149 # VM already running
150 if [ "$?" != 0 ]
151 then
152 # report VM is running
153 log_warning_msg "VM '$1' already running"
154 [ "$RETVAL" = 0 ] && RETVAL=1
155 continue
156 fi
157
158 # start VM
159 USER=$VBOX_USER LOGNAME=$VBOX_USER start-stop-daemon \
160 --start \
161 --quiet \
162 --pidfile $VM_PIDFILE \
163 --make-pidfile \
164 --background \
165 --chuid $VBOX_USER \
166 --startas $VM_DAEMON \
167 -- $VM_DAEMON_ARGS
168
169 log_action_end_msg "$?"
170
171 # check if start failed
172 if [ "$?" != 0 ]
173 then
174 # report error
175 log_failure_msg "Error starting VM '$VM' :("
176 RETVAL=2
177 fi
178
179 RETVAL="$?"
180
181 log_action_end_msg "$RETVAL"
182 exit 0
183 }
184
185 if [ -n "$1" ]
186 then
187 for VM in $VBOX_LIST
188 do
189 if [ "$VM" = "$1" ]
190 then
191 VM_EXISTS=1
192 fi
193 done
194
195 if [ "$VM_EXISTS" -eq "$TRUE" ]
196 then
197 start_vm $1
198 else
199 log_failure_msg "VM $1 is not a registered VM"
200 echo "Enter one VM name from the list below[VM names are case sensitive];"
201
202 for VM_1 in $VBOX_LIST
203 do
204 echo "$VM_1"
205 done
206 exit 1
207 fi
208
209 VM_EXISTS=0
210 check_battery
211 return
212 fi
213
214 # Start all VMs
215 for VM in $VBOX_LIST
216 do
217 start_vm $VM
218 done
219
220 if [ "$RETVAL" -lt 2 ]
221 then
222 log_daemon_msg "VirtualBox daemon started successfully"
223 else
224 log_daemon_msg "VirtualBox daemon started with errors"
225 fi
226
227 check_battery
228
229 return "$RETVAL"
230 }
231
232 #
233 # Function that stops the daemon/service
234 #
235 do_stop()
236 {
237 vm_init_list 1
238
239 # Return
240 # 0 if daemon has been stopped
241 # 1 if daemon was already stopped
242 # 2 if daemon could not be stopped
243 # other if a failure occurred
244 RETVAL=0
245 VM_EXISTS=0
246 FALSE=0
247 TRUE=1
248
249 #Function "safely" stops and running VM instance. Call this function in a
250 #loop to stop all running VM instances (Not the best way but still good)
251 stop_vm()
252 {
253 VM=$1
254 VM_UUID=`vm_get_uuid $VM`
255 VM_PIDFILE="$PIDFILE.$VM_UUID"
256
257 log_action_begin_msg "Stopping VM '$VM'"
258
259 # try savestate halt
260 vm_ctrl $VM savestate
261
262 # stop daemon
263 USER=$VBOX_USER LOGNAME=$VBOX_USER start-stop-daemon \
264 --stop \
265 --quiet \
266 --retry=TERM/30/KILL/5 \
267 --pidfile $VM_PIDFILE
268
269 case "$?" in
270 0)
271 log_action_end_msg 0
272 ;;
273 1)
274 log_warning_msg "VM '$VM' already stopped"
275 [ "$RETVAL" = 0 ] && RETVAL=1
276 ;;
277 2)
278 log_action_end_msg 1
279 log_failure_msg "ERROR: Could not stop VM '$VM'"
280 RETVAL=2
281 continue
282 ;;
283 esac
284
285 rm -f $VM_PIDFILE
286 }
287
288 if [ -n "$1" ]
289 then
290 for VM in $VBOX_LIST
291 do
292 if [ "$VM" = "$1" ]
293 then
294 VM_EXISTS=1
295 fi
296 done
297
298 if [ "$VM_EXISTS" -eq "$TRUE" ]
299 then
300 stop_vm $1
301 else
302 log_failure_msg "VM $1 is not a registered VM"
303 echo "Enter the VM name from the list of running VMS below;"
304
305 for VM_1 in $VBOX_LIST
306 do
307 echo "$VM_1"
308 done
309
310 exit 1
311 fi
312
313 VM_EXISTS=0
314 return
315 fi
316
317 for VM in $VBOX_LIST
318 do
319 stop_vm $VM
320 done
321
322 if [ "$RETVAL" -lt 2 ]
323 then
324 log_daemon_msg "VirtualBox daemon stopped successfully"
325 else
326 log_daemon_msg "VirtualBox daemon stopped with errors"
327 fi
328
329 return "$RETVAL"
330 }
331
332 #
333 # Function that sends a SIGHUP to the daemon/service
334 #
335 do_reload() {
336 #
337 # If the daemon can reload its configuration without
338 # restarting (for example, when it is sent a SIGHUP),
339 # then implement that here.
340 #
341 start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
342 return 0
343 }
344
345 check_battery()
346 {
347 BATTERY_OK=true
348 BATTERY_LEVEL=0
349
350 while [ $BATTERY_OK ]
351 do
352 BATTERY_LEVEL=`acpi | awk '{print $4}' | tr -d "%,"`
353 sleep 5
354
355 if [ $BATTERY_LEVEL -le $BATTERY_THRESHOLD ]
356 then
357 echo "Low Battery! Halting any running VMs" | wall
358 do_stop
359 BATTERY_OK=false
360 log_warning_msg "Battery is critically low: $BATTERY_LEVEL%"
361 fi
362 done
363 }
364
365 case "$1" in
366 start)
367 log_daemon_msg "Starting $DESC" "$NAME"
368 do_start $2
369 case "$?" in
370 0|1) log_end_msg 0 ;;
371 2) log_end_msg 1 ;;
372 esac
373 ;;
374 stop)
375 log_daemon_msg "Stopping $DESC" "$NAME"
376 do_stop $2
377 case "$?" in
378 0|1) log_end_msg 0 ;;
379 2) log_end_msg 1 ;;
380 esac
381 ;;
382 status)
383 status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
384 ;;
385 #reload|force-reload)
386 #
387 # If do_reload() is not implemented then leave this commented out
388 # and leave 'force-reload' as an alias for 'restart'.
389 #
390 #log_daemon_msg "Reloading $DESC" "$NAME"
391 #do_reload
392 #log_end_msg $?
393 #;;
394 restart|force-reload)
395 #
396 # If the "reload" option is implemented then remove the
397 # 'force-reload' alias
398 #
399 log_daemon_msg "Restarting $DESC" "$NAME"
400 do_stop
401 case "$?" in
402 0|1)
403 do_start $2
404 case "$?" in
405 0) log_end_msg 0 ;;
406 1) log_end_msg 1 ;; # Old process is still running
407 *) log_end_msg 1 ;; # Failed to start
408 esac
409 ;;
410 *)
411 # Failed to stop
412 log_end_msg 1
413 ;;
414 esac
415 ;;
416 *)
417
418 echo "Usage: $SCRIPTNAME {start [VM]|stop [VM]|status|restart [VM]|force-reload [VM]}" >&2
419 echo "\t Please note that 'start' [VM] indicates that the argument VM is optional f.g. you can write start OMRS"
420 exit 3
421 ;;
422 esac
423
424 :
425