#!/bin/sh
#-------------------------------------------------------------------------+
# Copyright (C) 2015 Matt Churchyard (churchers@gmail.com)
# All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted providing that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# 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.

# make sure we have the right environment
#
__setup(){
	__load_module "vmm"
	__load_module "nmdm"
	__load_module "if_bridge"
	__load_module "if_tap"

	sysctl net.link.tap.up_on_open=1 >/dev/null 2>&1
}

# load a kernel module
#
# @param string _mod the module name
#
__load_module(){
	local _mod="$1"
	kldstat -n ${_mod} >/dev/null 2>&1
	if [ $? -ne 0 ]; then
		kldload ${_mod} >/dev/null 2>&1
		[ $? -ne 0 ] && __err "unable to load ${_mod}.ko!"
	fi
}

# restart a local service
# checks if service is running and either starts or restarts
#
# @param string _serv the name of the service
#
__restart_service(){
	local _serv="$1"

	service ${_serv} status >/dev/null 2>&1

	if [ $? -ne 0 ]; then
		service ${_serv} start >/dev/null 2>&1
	else
		service ${_serv} restart >/dev/null 2>&1
	fi

	[ $? -ne 0 ] && __warn "failed to start/restart service ${_serv}"
}

# show version & usage information
# we exit after running this
#
__usage(){
cat << EOT
vm: Bhyve virtual machine managament v${VERSION}
Usage: vm ...
	init
	switch list
        switch info [name]
	switch create <name>
	switch import <name> <bridge>
	switch vlan <name> <vlan|0>
	switch nat <name> <on|off>
	switch add <name> <interface>
	switch remove <name> <interface>
	switch destroy <name>
	list
        info [name]
	create [-t template] [-s size] <name>
	install <name> <iso>
	start <name>
	stop <name>
	console <name> [com1|com2]
	rename <name> <new-name>
	add [-d device] [-t type] [-s size|switch] <name>
	startall
	stopall
	reset <name>
	poweroff <name>
	configure <name>
	destroy <name>
	clone <name> <new-name>
	snapshot [-f] <name[@snapshot]>
	rollback [-r] <name@snapshot>
	iso [url]
	image list
	image create [-d description] <name>
	image destroy <uuid>
	image provision <uuid> <newname>
EOT
	exit 1
}

# error
# display an error message an exit immediately
#
# @param string - the message to display
#
__err(){
	echo "${0}: ERROR: $1"
	exit 1
}

# warn
# display warning, but do not exit
#
# @param string - the message to display
#
__warn(){
	echo "${0}: WARNING: $1"
}

# log to file
# writes the date and a message to the specified log
# the global log is in $vm_dir/vm-bhyve.log
# guest logs are $vm_dir/{guest}/vm-bhyve.log
#
# @param string _type=guest|system log to global vm-bhyve log or guest
# @param string _guest if _type=guest, the guest name, otherwise do not provide at all
# @param string _message the message to log
#
__log(){
	local _type="$1"
	local _lf="vm-bhyve.log"
	local _guest _message _file _date

	case "${_type}" in
		guest)
			_guest="$2"
			_message="$3"
			_file="${vm_dir}/${_guest}/${_lf}"
			;;
		system)
			_message="$2"
			_file="${vm_dir}/${_lf}"
			;;
	esac

	_date=$(date +"%b %d %T")
	echo "${_date}: ${_message}" >> "${_file}"
}

# write content to a file, and log what we
# did to the guest log file
# it's useful to be able to see what files vm-bhyve is creating
# and the contents so we write that to the log.
# The file is created in $vm_dir/{guest}
#
# @param string _type=write|appnd create file or append to it
# @param string _guest the guest name
# @param string _file the file name to write to
#
__log_and_write(){
	local _type="$1"
	local _guest="$2"
	local _file="$3"
	local _message="$4"

	_file="${vm_dir}/${_guest}/${_file}"

	if [ "${_type}" = "write" ]; then
		__log "guest" "${_guest}" "create file ${_file}"
		echo "${_message}" > "${_file}"
	else
		echo "${_message}" >> "${_file}"
	fi

	__log "guest" "${_guest}" " -> ${_message}"
}

# confirm yes or no
#
# @param string _msh message to display
# @return int success if confirmed
#
__confirm(){
	local _msg="$1"
	local _resp

	while read -p "${_msg} (y/n)? " _resp; do
		case "${_resp}" in
			y*)
				return 0
				;;
			n*)
				return 1
				;;
		esac
	done
}

# our own checkyesno copy
# doesn't warn for unsupported values
# also returns as 'yes' unless value is specifically no/off/false/0 
#
# @param _value the value to test
# @return int 1 if set to "off/false/no/0", 0 otherwise
#
__checkyesno(){
	local _value="$1"

	case "$_value" in
		[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
			return 1
			;;
		*)
			return 0
			;;		
	esac
}
