Let's see. I would use
vboxmanage list vms to list all known VMs, and
vboxmanage list runningvms to check which ones are running.
To start a VM, you need to use the command I showed earlier, to detach it from the terminal, otherwise it will be killed when the terminal closes. In other words,
Code:
( setsid vboxheadless --startvm "$VM" --vrde off </dev/null &>/dev/null & )
To stop that VM, you only need to run
Code:
vboxmanage controlvm "$VM" poweroff
Here is the basic skeleton script I'd start with:
Code:
#!/bin/bash
# Only split tokens at newlines.
IFS=$'\n'
# Helper constants to be used in glob patterns
WSQ=$'[\t\n\v\f\r \042\047]' # Whitespace or quotes
WSQB=$'[\t\n\v\f\r \042\047\173\175]' # Whitespace, quotes or braces
# Update VM list.
VMRefresh () {
VMCount=0 # Number of VMs
VMID=() # List of VM ID's
VMDesc=() # List of VM descriptions
VMRunning=() # "TRUE" if the VM is currently running
local line="" desc="" code="" temp="" count=0
while read line ; do
# Split the line at the opening brace, {.
desc="${line%%\{*}"
code="${line##*\{}"
# Remove leading and trailing whitespace and quotes from desc.
temp=""
while [ "$desc" != "$temp" ]; do
temp="$desc"
desc="${desc#$WSQ}"
desc="${desc%$WSQ}"
done
# Remove leading and trailing whitespace, quotes and braces from desc.
temp=""
while [ "$code" != "$temp" ]; do
temp="$code"
code="${code#$WSQB}"
code="${code%$WSQB}"
done
# Add to VMList.
VMID[VMCount]="$code"
VMDesc[VMCount]="$desc"
VMRunning[VMCount]="FALSE"
VMCount=$((VMCount+1))
done < <( vboxmanage list vms )
# Check which ones are currently running.
while read line ; do
# Split the line at the opening brace, {.
code="${line##*\{}"
# Remove leading and trailing whitespace, quotes and braces from desc.
temp=""
while [ "$code" != "$temp" ]; do
temp="$code"
code="${code#$WSQB}"
code="${code%$WSQB}"
done
# Find the matching ID, and mark it running.
for (( i = 0; i < VMCount; i++ )); do
if [ "${VMID[i]}" = "$code" ]; then
VMRunning[i]="TRUE"
break
fi
done
done < <( vboxmanage list runningvms )
return 0
}
# Initialize VM list.
VMCount=0
VMID=()
VMDesc=()
VMRunning=()
VMRefresh
# Construct a list for zenity.
ZList=()
for (( vm = 0; vm < VMCount; vm++ )); do
ZList[${#ZList[@]}]="${VMRunning[vm]}"
if [ "${VMRunning[vm]}" = "TRUE" ]; then
ZList[${#ZList[@]}]="Yes"
else
ZList[${#ZList[@]}]="No"
fi
ZList[${#ZList[@]}]="${VMDesc[vm]}"
ZList[${#ZList[@]}]="${VMID[vm]}"
done
# Ask the user which VMs to start/keep running.
if ! VMRun=($( zenity \
--list --checklist \
--title="VM Management" \
--width="800" --height="600" \
--text="Which VMs should run?" \
--separator=$'\n' \
--column="Run" --column="Running" --column="Virtual machine" --column="ID" \
--hide-column="4" --print-column="4" \
"${ZList[@]}"
) ) ; then
printf 'Cancelled.\n' >&2
exit 0
fi
# Construct a list of VM IDs that are to be started, not yet running.
VMStart=()
for id in "${VMRun[@]}" ; do
for (( i = 0; i < VMCount; i++ )); do
if [ "${VMID[i]}" = "$id" ]; then
if [ "${VMRunning[i]}" = "FALSE" ]; then
VMStart[${#VMStart[@]}]="$id"
fi
break
fi
done
done
# Construct a list of VM IDs that are running, but should be stopped.
VMStop=()
for (( i = 0; i < VMCount; i++ )); do
if [ "${VMRunning[i]}" = "TRUE" ]; then
run=0
for id in "${VMRun[@]}" ; do
if [ "$id" = "${VMID[i]}" ]; then
run=1
break
fi
done
if [ $run -eq 0 ]; then
VMStop[${#VMStop[@]}]="$id"
fi
fi
done
#
# TODO: Modify the rest of the script!
#
# Start the new VMs.
for id in "${VMStart[@]}" ; do
( setsid vboxheadless --startvm "$id" --vrde off </dev/null &>/dev/null & )
done
# Stop the desired VMs.
for id in "${VMStop[@]}" ; do
vboxmanage controlvm "$id" poweroff
done
As it is now, it will start and stop the VMs, but it will not monitor their status. It is easy enough to add to the script, but since there are many ways to do it properly, I didn't want to impose one at random. (I wrote the
VMRefresh as a shell function, so the monitoring part can simply call it every second or so, to detect the changes in the VMs. You'll also need a loop or two to check if the desired VMs have been started/stopped.)
It is not at all difficult to use another zenity window to monitor the status of the started machines, and the shutdown progress for the stopped VMs. Or you can use one window for started VMs, and one for stopped VMs. They can be made to close automatically after a specific timeout. Or you can use a terminal. Or you can use
notify-send to pop up notification messages about any changes the script sees.
It would be very simple to use a zenity window to report the status of all the VMs, with "
Not running" for those that the user did not select to be started, "
Running" for those that the user kept running, "
Starting" and "
Started" for those the user wanted started, and "
Stopping"/"
Stopped" for those the user wants stopped. The user can dismiss the window at any time; the virtual machines have already been asked to do what the user wanted, and the script and the window just monitor what is actually happening.
Could you run the above script, and tell me if the VM selection is what you had in mind? Or if you want it text-based, without zenity? Or in some other form? Note that clicking "Cancel" in the zenity window means the script will not start or stop any VMs.
After the selection is the way you want it, you can describe if/how you want the VM startup/poweroff to be monitored, and we can modify the script to do it that way.
Because the VM bootup process can take a minute or two, I'd use a bit trickier approach: I'd use a small shared folder across all VMs, and have each VM create an empty file there at the end of the boot process, named after the VM (using eg. a very simple
/etc/init.d/bootup-ok script in Linux, something else for other OSes). Then, this script could first remove the file before starting the VM, and wait for the file to appear (during waiting the status would be "booting.." or something); after the file is seen, we know the VM has booted up properly. If it does not appear in say ten or fifteen minutes, there is something wrong, and the user can be alerted to check it.