#!/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.

# __guest_*
# these functions are responsible for doing any pre-load tasks for a guest
# for non uefi guests this normally means running bhyveload or grub-bhyve
# these functions should update _exit to a non-zero value if there's a problem
# or 0 on success
# As these functions are called from within the scope of __vm_run,
# the following variables are already set (among others)
#
# _name: guest name
# _com: com port - /dev/nmdmXA
# _iso: iso file (if an install)
# _conf: full path to guest config file
# _cpu: cpu count
# _memory: RAM
# _guest: guest type
# _bootdisk: full path to primary disk
#
# we have every bhyveload/grub-bhyve call entered twice, once to log and
# once to actually run. bit of a pain but makes for useful logs.
#
# I've written append wrong as it just needs to be something other than 'write',
# and is much more readable when all the __log* calls line up
#
# @modifies _exit
# @param string _type=install|run whether this is an install or normal run
#

# BSDs

# FreeBSD
__guest_freebsd(){
	if [ "$1" = "install" ]; then
		__log "guest" "${_name}" "bhyveload -c ${_com} -m ${_memory} -d ${vm_dir}/.iso/${_iso} ${_name}"
		bhyveload -c "${_com}" -m "${_memory}" -d "${vm_dir}/.iso/${_iso}" "${_name}"
	else
		__log "guest" "${_name}" "bhyveload -c ${_com} -m ${_memory} -d ${_bootdisk} ${_name}"
		bhyveload -c "${_com}" -m "${_memory}" -d "${_bootdisk}" "${_name}"
	fi

	_exit=$?
}

# NetBSD
__guest_netbsd(){
	if [ "$1" = "install" ]; then
		__log_and_write "write" "${_name}" "device.map" "(cd0) ${vm_dir}/.iso/${_iso}"
		__log_and_write "appnd" "${_name}" "device.map" "(hd1) ${_bootdisk}"
		__log_and_write "write" "${_name}" "boot.cmd" "knetbsd -h -r cd0a (cd0)/netbsd"
		__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
		__log "guest" "${_name}" "grub-bhyve -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -r cd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
	else
		__log_and_write "write" "${_name}" "device.map" "(hd1) ${_bootdisk}"
		__guestpri_custom_grub && return 0
		__log_and_write "write" "${_name}" "boot.cmd" "knetbsd -h -r ld0a (hd1,msdos1)/netbsd"
		__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
		__log "guest" "${_name}" "grub-bhyve -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
	fi

	_exit=$?
}

# OpenBSD
# requires guest kernel version in config file
# one of the awkward guests that have version in kernel load path
__guest_openbsd(){
	local _gver _arch

	__config_get "_gver" "guest_version"
	__config_get "_arch" "arch"

	if [ -z "${_gver}" ]; then
		__log "guest" "${_name}" "openbsd machines require \"guest_version\" value in configuration"
		_exit=15
	else
		if [ "$1" = "install" ]; then
			__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
			__log_and_write "appnd" "${_name}" "device.map" "(cd0) ${vm_dir}/.iso/${_iso}"
			__log_and_write "write" "${_name}" "boot.cmd" "kopenbsd -h com0 /${_gver}/${_arch:-amd64}/bsd.rd"
			__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
			__log "guest" "${_name}" "grub-bhyve -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
			env -i TERM=xterm /usr/local/sbin/grub-bhyve -r cd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
		else
			__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
			__guestpri_custom_grub && return 0
			__log_and_write "write" "${_name}" "boot.cmd" "kopenbsd -h com0 -r sd0a (hd0,openbsd1)/bsd"
			__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
			__log "guest" "${_name}" "grub-bhyve -r hd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
			env -i TERM=xterm /usr/local/sbin/grub-bhyve -r hd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
		fi

		_exit=$?
	fi
}

# LINUXes

# Alpine
__guest_alpine(){
	if [ "$1" = "install" ]; then
		__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
		__log_and_write "appnd" "${_name}" "device.map" "(cd0) ${vm_dir}/.iso/${_iso}"
		__log_and_write "write" "${_name}" "boot.cmd" "linux /boot/grsec initrd=/boot/initramfs-grsec alpine_dev=cdrom:iso9660 modules=loop,squashfs,sd-mod,usb-storage,sr-mod"
		__log_and_write "appnd" "${_name}" "boot.cmd" "initrd /boot/initramfs-grsec"
		__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
		__log "guest" "${_name}" "grub-bhyve -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -r cd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
	else
		__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
		__guestpri_custom_grub && return 0
		__log_and_write "write" "${_name}" "boot.cmd" "linux /boot/vmlinuz-grsec root=/dev/vda3 modules=ext4"
		__log_and_write "appnd" "${_name}" "boot.cmd" "initrd /boot/initramfs-grsec"
		__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
		__log "guest" "${_name}" "grub-bhyve -r hd0,msdos1 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -r hd0,msdos1 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
	fi

	_exit=$?
}

# CentOS
__guest_centos(){
	local _kernel=

	__config_get "_kernel" "linux_kernel"

	if [ -z "${_kernel}" ]; then
		__log "guest" "${_name}" "centos machines require \"linux_kernel\" value in configuration"
		_exit=15
	else
		if [ "$1" = "install" ]; then
			__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
			__log_and_write "appnd" "${_name}" "device.map" "(cd0) ${vm_dir}/.iso/${_iso}"
			__log_and_write "write" "${_name}" "boot.cmd" "linux /isolinux/vmlinuz"
			__log_and_write "appnd" "${_name}" "boot.cmd" "initrd /isolinux/initrd.img"
			__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
			__log "guest" "${_name}" "grub-bhyve -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
			env -i TERM=xterm /usr/local/sbin/grub-bhyve -r cd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
		else
			__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
			__guestpri_custom_grub && return 0
			__log_and_write "write" "${_name}" "boot.cmd" "linux /vmlinuz-${_kernel} root=/dev/mapper/centos-root"
			__log_and_write "appnd" "${_name}" "boot.cmd" "initrd /initramfs-${_kernel}.img"
			__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
			__log "guest" "${_name}" "grub-bhyve -r hd0,msdos1 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
			env -i TERM=xterm /usr/local/sbin/grub-bhyve -r hd0,msdos1 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
		fi

		_exit=$?
	fi
}

# CentOS - using grub bootloader inside guest
__guest_centos-grub(){
	if [ "$1" = "install" ]; then
		__guest_centos "$1"
		return
	else
		__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
		__guestpri_custom_grub && return 0
		__log_and_write "write" "${_name}" "boot.cmd" ""
		__log "guest" "${_name}" "grub-bhyve -r hd0,msdos1 -m ${vm_dir}/${_name}/device.map -d /grub -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -r hd0,msdos1 -m "${vm_dir}/${_name}/device.map" -d "/grub" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
	fi

	_exit=$?
}

# Debian
__guest_debian(){
	__guest_linux "$1"
}

# Ubuntu
__guest_ubuntu(){
	__guest_linux "$1"
}

# Arch Linux
__guest_arch(){
	local _date=$(date +%Y%m)

	if [ "$1" = "install" ]; then
		__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
		__log_and_write "appnd" "${_name}" "device.map" "(cd0) ${vm_dir}/.iso/${_iso}"
		__log_and_write "write" "${_name}" "boot.cmd" "linux (cd0)/arch/boot/x86_64/vmlinuz archisobasedir=arch archisolabel=ARCH_${_date} ro"
		__log_and_write "appnd" "${_name}" "boot.cmd" "initrd (cd0)/arch/boot/x86_64/archiso.img"
		__log_and_write "appnd" "${_name}" "boot.cmd" "boot"
		__log "guest" "${_name}" "grub-bhyve -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/boot.cmd"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -r cd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/boot.cmd"
	else
		__guest_linux "$1"
		return
	fi

	_exit=$?
}

# Basic Linux
__guest_linux(){
	if [ "$1" = "install" ]; then
		__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
		__log_and_write "appnd" "${_name}" "device.map" "(cd0) ${vm_dir}/.iso/${_iso}"
		__log "guest" "${_name}" "grub-bhyve -c ${_com} -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name}"
		/usr/local/sbin/grub-bhyve -c "${_com}" -r cd0 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}"
	else
		__log_and_write "write" "${_name}" "device.map" "(hd0) ${_bootdisk}"
		__guestpri_custom_grub && return 0
		__log "guest" "${_name}" "grub-bhyve -c ${_com} -r hd0,msdos1 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name}"
		/usr/local/sbin/grub-bhyve -c "${_com}" -r hd0,msdos1 -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}"
	fi	

	_exit=$?
}

# Run custom grub commands if available
__guestpri_custom_grub(){
	local _grub

	__config_get "_grub" "grub_commands"

	if [ -n "${_grub}" ]; then
		__log "guest" "${_name}" "using grub commands from ${vm_dir}/${_name}/${_grub}"
		__log "guest" "${_name}" "grub-bhyve -r cd0 -m ${vm_dir}/${_name}/device.map -M ${_memory} ${_name} < ${vm_dir}/${_name}/${_grub}"
		env -i TERM=xterm /usr/local/sbin/grub-bhyve -m "${vm_dir}/${_name}/device.map" -M "${_memory}" "${_name}" < "${vm_dir}/${_name}/${_grub}"
		_exit=$?
		return 0
	fi

	return 1
}

# Windows

# No loader needed for any Win version
__guest_windows(){
	_exit=0
}

# Generic guest
# With uefi, many guests may not need any special handling
__guest_generic(){
	_exit=0
}
