Virtual Box is a virtualization software that allows many operating systems (guests) to run simultaneously on a host operating system. In the concept of cloud computing, it is increasingly common for server operating systems to be run in a virtual machine. The guest operating systems can be run in either headed or headless modes. The difference is that in "Headed" mode, the guest displays an interface similar to that displayed when the monitor is connected to the system.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