#!/bin/bash

# Copyright 2018 Nikos Giotis, Athens, GR
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# vms command sript

VMS_LIB=/var/lib/vms

vms_create() {
    name=$1
    template=$2
    if [ "$name" == "" ]; then
        echo 'Usage: vms create <name>'
        exit 1
    fi
    vmdir="$HOME/.vms/$name"
    if [ -d "$vmdir" ]; then
        echo "$name already exists"
        exit 1
    fi
    mkdir -p $vmdir
    vmsconf="$HOME/.vms/vms.conf"
    if [ ! -f $vmsconf ]; then
        echo '#!/bin/bash' >> $vmsconf
    fi
    vms_stub $name
    cp "$VMS_LIB/vm.sh" "$vmdir/$name.sh"
    if [ "$template" != "" ]; then
        echo "Using $template template"
        if [ ! -d "$VMS_LIB/templates/$template" ]; then
            echo "$template does not exist"
            exit 1
        fi
    fi
}

vms_stub() {
    name=$1
    vmsconf="$HOME/.vms/vms.conf"
    cat << EOF >> $vmsconf

declare -A $name
$name[name]='$name'
$name[uuid]='$(uuidgen)'
$name[arch]='$(uname -m)'
EOF

}

. /usr/bin/vms-parse

vms_info() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms info <name>'
        exit 1
    fi
    info_VM $name
}

vms_conf() {
    name=$1; key=$2; value="$3"
    if [ "$name" == "" ] || [ "$key" == "" ] || [ "$value" == "" ]; then
        echo 'Usage: vms conf <name> <key> <value>'
        exit 1
    fi

    valid_VM $name

    IFS=$'\n'
    # Find all the lines that start with $name[]
    lines=$(<"$HOME/.vms/vms.conf")
    keylines=()
    lastclean=""
    for l in $lines; do
        clean=$( echo $l |  grep -v -E "^\s*#.*" )
        if [ "$clean" != "" ] || [ "$clean" != "\n" ]; then
            if echo $clean | grep -E "^\s*$name\[.*].*" >/dev/null; then
                lastclean=$clean
                keyl=$( echo $clean | grep -E "^\s*$name\[$key].*" )
                if [ "$keyl" != "" ]; then
                    keylines+=($keyl)
                fi
            fi
        fi
    done

    if [ ${#keylines[@]} -gt 1 ]; then
        echo "Found more than one match and will exit!"
        for l in ${keylines[@]}; do echo $l; done
        exit 1
    fi

    newline="$name\[$key]=$value"
    if [ "$keylines" != "" ]; then
        # Found existing $key, modify it
        oldline="${keylines/'['/'\['}"
        if [ "$value" == "--" ]; then
            # User requested delete
            required_keys="name uuid arch pid"
            if echo "$required_keys" | grep -w $key > /dev/null; then
                echo "You should not delete $key"
            else
                sed -i "/$oldline/d" ~/.vms/vms.conf
                echo "Deleted $keylines"
            fi
        else
            if [ "$oldline" == "$newline" ]; then
                echo "$keylines is already set"
            else
                sed -i "s|$oldline|$newline|1" ~/.vms/vms.conf
                echo "Modified $keylines -> ${newline/'\['/'['}"
            fi
        fi
    else
        # $key was not found, add a new one
        if [ "$value" == "--" ]; then
            echo "Can't find $key"
        else
            lastkey=${lastclean##*[}
            lastkey=${lastkey%]*}
            sed -i "/\s*$name\[$lastkey]/a $newline" ~/.vms/vms.conf
            echo "Added ${newline/'\['/'['}"
        fi
    fi
}

vms_status() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms status <name>'
        exit 1
    fi
    status_VM $name
    if [ $? -eq 0 ]; then
        echo "$name is not running"
    fi
}

vms_list() {
    for f in $(ls $HOME/.vms/*/*.sh) ; do
        vm=$(basename "$f" | rev | cut -c 4- | rev )
        echo -n $vm
        status=$( status_VM $vm )
        if [ "$status" != "" ]; then
            echo -n " [R]"
        fi
        echo
    done
}

vms_start() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms start <name>'
        exit 1
    fi
    if [ "$( secure_VM $name)" == "no" ]; then
        echo "vms start $name must be run as root via rc.vms"
        exit 1
    fi
    vmdir="$HOME/.vms/$name"
    if [ -f "$vmdir/$name.sh" ]; then
        echo -n "starting $name ..."
    ret=$(bash "$vmdir/$name.sh")
        if [ "$ret" != "" ]; then
            echo " error!"
        echo $ret
        exit 1
        else
            echo " done."
        fi
    else
        echo "$name does not exist"
        exit 1
    fi
}

vms_stop() {
    name=$1
    timeout=$2
    if [ "$name" == "" ]; then
        echo 'Usage: vms status <name>'
        exit 1
    fi
    pid=$(process_VM $name)
    if [ "$( secure_VM $name)" == "no" ]; then
        echo "vms stop $name must be run as root via rc.vms"
        exit 2
    fi
    if [ "$pid" != "" ]; then
        echo -n "stopping $name ($pid) .."
        while ps -p "$pid" > /dev/null; do
            echo -n '.'
            monitor=$( rundir_VM $name ).monitor
            if [ ! -w $monitor ]; then
                echo "Use ${name}[monitor]=yes to send ACPI shutdown event"
                exit 1
            fi
            total_time=0
            echo 'system_powerdown' | socat "unix-connect:$monitor" stdin
            wait_time=0
            while ps -p "$pid" > /dev/null && [ $wait_time -lt 50 ]; do
                sleep 1
                ((wait_time++))
                ((total_time++))
                if [ "$timeout" != "" ]; then
                    if [ $total_time -gt $timeout ]; then
                        echo -n '.'
                        vms_kill $name
                        echo " killed after $timeout!"
                    fi
                fi
            done
        done
        vms_clean_files
        echo " done."
    else
        echo "$name is not running"
    fi
}

vms_clean_files() {
    rundir=$( rundir_VM $name )
    if [ "$rundir" != "" ] && [ -w "$rundir" ]; then
        rm -f "$rundir.monitor"
        rm -f "$rundir.serial"
        rm -f "$rundir.pid"
        rmdir "$rundir"
    fi
}

vms_kill() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms kill <name>'
        exit 1
    fi
    vmdir="$HOME/.vms/$name"
    pid=$( process_VM $name )
    if [ "$pid" != "" ]; then
        echo -n "killing $1 ... "
        kill $pid
        if [ $? -eq 0 ]; then
            echo 'done.'
            vms_clean_files
        else
            echo 'ERROR!'
        fi
    else
        echo "$name is not running"
    fi
}

vms_restart() {
    name=$1
    timeout=$2
    if [ "$name" == "" ]; then
        echo 'Usage: vms kill <name>'
        exit 1
    fi
    vms_stop $name $timeout
    vms_start $name
}

vms_log() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms log <name>'
        exit 1
    fi
    logfile="$( rundir_VM $name ).log"
    if [ -r "$logfile" ]; then
        tail "$logfile"
    else
        echo "can't access log"
    fi
}

vms_monitor() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms monitor <name>'
        exit 1
    fi
    monitor="$( rundir_VM $name ).monitor"
    if [ -w "$monitor" ]; then
        unixterm "$monitor"
    else
        echo "Can't access $monitor"
    fi
}

vms_serial() {
    name=$1
    if [ "$name" == "" ]; then
        echo 'Usage: vms serial <name>'
        exit 1
    fi
    serial="$( rundir_VM $name ).serial"
    if [ -w "$serial" ]; then
        minicom -D "unix#$serial"
    else
        echo "Can't access serial"
    fi
}

vms_vnc() {
    name=$1; verbose=$2
    if [ "$name" == "" ]; then
        echo 'Usage: vms vnc <name>'
        exit 1
    fi
    vnc=$(vnc_VM $name)
    if [ "$vnc" != "" ]; then
        if [ "$verbose" == "-v" ]; then
            vncviewer :$vnc &
        else
            vncviewer :$vnc 1>/dev/null 2>&1 &
        fi
    else
        echo "$name does not have VNC server"
    fi
}


case "$1" in
 'create') vms_create $2 $3 ;;
   'list') vms_list ;;
   'info') vms_info $2 ;;
   'conf') vms_conf $2 $3 "$4" ;;
 'status') vms_status $2 ;;
  'start') vms_start $2 ;;
   'kill') vms_kill $2 ;;
   'stop') vms_stop $2 $3 ;;
'restart') vms_restart $2 $3 ;;
    'log') vms_log $2 ;;
'monitor') vms_monitor $2 ;;
 'serial') vms_serial $2 ;;
    'vnc') vms_vnc $2 $3 ;;
*) echo "Usage:
vms create <vm_name> [vm_template]
vms list
vms info <vm_name>
vms conf <vm_name> <key> <value>
vms status <vm_name>
vms start <vm_name>
vms kill <vm_name>
vms stop <vm_name> [timeout]
vms restart <vm_name> [timeout]
vms log <vm_name>
vms monitor <vm_name>
vms serial <vm_name>
vms vnc <vm_name> [-v]"
esac
