#!/bin/sh

# Copyright (c) 2015, pr1ntf (Trent Thompson) All rights reserved.
# Copyright (c) 2016, Justin D Holcomb All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.

# Displays active tap via /dev
__list_active_taps() {
	echo "Listing active network taps..."
	ls /dev | grep tap
}

# List bridges
__list_bridges() {
	__get_bridge_list_on_chyves
	local _GDP_bridge=$( __return_property_value_from_config_file "defaults" "bridge" )

	for b in `echo "$_BRIDGE_list_on_chyves" | tr '\n' ' '`
	do
		echo "Bridge: $b"

		# Get outside interface for this bridge
		local _phy=$( __return_property_value_from_config_file "global" "${b}_phy_attach" )
		[ "$_phy" = "" ] && __fault_detected_exit "Bridge not associated with physical or vlan interface, or set to \"private\". \nPlease run: chyves network $b attach {iface} \n        Or: chyves network $b private"
		__verify_valid_iface_format $_phy
		[ "$b" = "$_GDP_bridge" ] && echo "       Default bridge for new guests."
		if [ "$_IFACE_type" = "private" ]; then
			echo "       Private network, no outside connectivity."
		else
			echo "       $_IFACE_type interface: "
			echo "             $_phy"
		fi

		# Get taps for this bridge
		__get_tap_list_for_bridge_on_chyves $b
		echo "       taps: "
		for pat in $_TAP_list_for_bridge_on_chyves
		do
			echo "             $pat"
		done
	done
}

# List clones
__list_clones() {
	local _clone_parents _last_parents
	_CLONE_tiers=0

	# Find all ZFS datasets with clones associated with them
	local _clone_parents="$( zfs get -H -r -o name,value origin | grep /chyves/guests/ | awk '$2 != "-" {print $2}' | cut -d'/' -f4 | sort | uniq | tr '\n' ' ' )"

	[ "$_clone_parents" = "" ] && echo "No clones on host" && exit

	echo "Hierarchical overview of clones:"

	# Find guests that have clones but are not clones themselves. Adam and Eve if you will.
	while [ 1 ]
	do
		[ "$_last_parents" = "$_clone_parents" ] && break

		for _parent in `echo $_clone_parents`
		do
			for _child in `zfs get -H -t volume -r -o name,value origin | awk -v pattern="/chyves/guests/$_parent/" '$2 ~ pattern {print $1}' | cut -d'/' -f4 | sort | uniq | tr '\n' ' '`
			do

				# Remove guests that are children
				local _clone_parents="$( echo $_clone_parents | tr ' ' '\n' | grep -w -v $_child | tr '\n' ' ' )"
			done
		done

		local _last_parents=$_clone_parents
	done

	# Spider through the Adam and Eve guests.
	for _child in `echo $_clone_parents`
	do
		__list_clones_sub "$_child"
	done
}

# Backend for 'chyves list clones', spiders through each supplied guest name for child clones.
__list_clones_sub() {
	local _this_parent=$1
	local i=0
	local incoming_tier=$_CLONE_tiers
	_CLONE_tiers="$( expr 1 + $_CLONE_tiers )"

	local _children="$( zfs get -H -t volume -r -o name,value origin | awk -v pattern="/chyves/guests/$_this_parent/" '$2 ~ pattern {print $1}' | cut -d'/' -f4 | uniq | tr '\n' ' ' )"

	# Create indentions based on current tier
	while [ "$i" -lt "$_CLONE_tiers" ]
	do
		echo -n "   "
		local i="$( expr 1 + $i )"
	done

	# Print the guest name
	echo "$_this_parent"

	# Inception for each guest with child clones
	if [ -n "$_children" ]; then
		for _gbaby in `echo $_children`
		do
			__list_clones_sub "$_gbaby"
		done
	fi

	# Reset the tier level to what it was when the function was called.
	_CLONE_tiers="$incoming_tier"
}

# List properties set in .defaults
__list_defaults() {
	(
	echo "Guest Default:^Value:"
	awk 'BEGIN { FS = "=" } ; { print $1"^"$2 }' $_DEFAULT_CONFIG_FILE | sort -k1
	) | column -ts^
}

# List Firmware resources. Using "firmware" lists the property firmware rather than the resource
__list_firmware() {
	(
	echo -e "UEFI Firmware^Size"
	zfs list -H -t filesystem -r -o name,used $_PRIMARY_POOL/chyves/Firmware | sed 1d | cut -d '/' -f 4- | awk '{ print $1"^"$2 }' | sort -k1
	) | column -ts^
}

# List properties set in .config
__list_global_properties() {
	local _pool=$1
	local _pool_list="$( __return_pools_all | tr '\n' ' ' )"

	[ "$_pool" = "primary" ] && _pool=$_PRIMARY_POOL

	# If pool is specified, only display that one.
	[ -n "$_pool" ] && local _pool_list="$_pool"

	(
	echo "Pool:^Type:^Property:^Value:"
	for _loop in $_pool_list
	do
		echo $_loop
		if [ -e "/chyves/$_loop/.config/global.cfg" ]; then
			echo " ^Global"
			awk 'BEGIN { FS = "=" } ; { print " ^ ^"$1"^"$2 }' /chyves/$_loop/.config/global.cfg | sort -k1
		fi

		if [ -e "/chyves/$_loop/.config/pool.cfg" ]; then
			echo " ^Pool"
			awk 'BEGIN { FS = "=" } ; { print " ^ ^"$1"^"$2 }' /chyves/$_loop/.config/pool.cfg | sort -k1
		fi
	done
	) | column -ts^
}

# List ISO firmware resources.
__list_iso() {
	(
	echo -e "ISO^Size"
	zfs list -H -r -o name,used $_PRIMARY_POOL/chyves/ISO | cut -d '/' -f 4- | grep -v -E '^$' | awk '{ print $1"^"$2 }' | sort -k1
	) | column -ts^
}

# List pools and roles
__list_pools() {
	local _pool_list="$( __return_pools_all )"
	(
		echo "Pool:^Role:^Version:"
	(
		for _pool in $_pool_list
		do
			local _dataset_role=$( __return_property_value_from_config_file "manual" "dataset_role" "/chyves/$_pool/.config/pool.cfg" )
			local _dataset_version=$( __return_property_value_from_config_file "manual" "dataset_version" "/chyves/$_pool/.config/pool.cfg" )
			echo "$_pool^$_dataset_role^$_dataset_version"
		done
	) | sort -k1
	) | column -ts^
}

# List processes
__list_processes() {
	echo "Listing processes..."
	if [ -z "$_GUEST_name" ]; then
		ps -aux | grep -E "chy-|chyves|bhyveload|grub-bhyve" | grep -v -E "grep -E chy-|/bin/sh /usr/local/sbin/chyves list"
	else
		ps -aux | grep "$_GUEST_name" | grep -E -v "grep ${_GUEST_name}|/bin/sh /usr/local/sbin/chyves list"
	fi
}

# Dynamically lists the guest chyves properties
__list_properties() {
	echo "Use the following syntax to display the specified property and a list values for each guest."
	echo "These are the available properties: "
	__return_property_list
	echo "Run 'chyves list <property>' where <property> is an above value."
}

# Lists a property for all active guests. See "chyves list properties" for options
__list_single_property() {
	local _property="$1"

	echo "Listing value of property ('$_property') for all guests."

	# Displays singular property
	(
	echo "Guest:^$_property:"
	for _pool in `__return_new_line_delimit_as_space_string $( __return_pools_all )`
	do
		for _guest in `__return_new_line_delimit_as_space_string $( __return_guest_list $_pool )`
		do
			_GUEST_name="$_guest"
			_GUEST_pool=$_pool
			_GUEST_config_file="/chyves/$_GUEST_pool/guests/$_GUEST_name/.config/.cfg"
			local _value="$( __return_property_value_from_config_file "guest" "$_property" )"
			echo "$_GUEST_name ^$_value"
		done
	done
	) | column -ts^
}

# List snapshots
__list_snapshots() {

	if [ -z "$_GUEST_name" ]; then
		echo "Listing top level snapshots..."
		zfs list -H -d 4 -t snapshot -o name | grep chyves/guests | grep -v ".config" | cut -d '/' -f4- | sed '/^$/d'
	else
		echo "Listing all $_GUEST_name snapshots..."
		__return_guest_snapshot_list_level2
	fi
}

# Display info about all guests.
__info() {
	local flags="$1"
	local _gst="$2"   # Back-end use only, only shows info for single guest.

	# Set
	if [ -z "$_gst" ]; then
		local _pools="$( __return_pools_active )"
	else
		_GUEST_name="$_gst"
		__gvset_guest_pool
		local _pools="$_GUEST_pool"
	fi

	if [ -z "$flags" ]; then
		if [ "$_CALLED_from_list" != 1 ]; then
			local flags="$_DEFAULT_INFO_FLAGS"
		fi
	fi

	if [ "$flags" = "-h" ]; then
cat << 'EOT'

    chyves info

Displays active guests and their disks on the system when no flags are used.
The global property 'default_info_flags' is used when no flags are use. This
property is populated with end user's preferred flags to use. The following
flags are used in combination to display information about the guests and
disks. These flags are used in combination, meaning the correct syntax is
"-rvs" rather than "-r -v -s". See below for a description of what each flag
does:

 Flags to controls which fields are displayed:
  -z    [size] Displays size, for guests this show what is being used by
        the dataset. For disks the size displays the volume size.
  -b    [basic] CPU core count, and RAM allocation.
  -p    [pool] Display the pool for the guest.
  -r    [role] Display what role the pool is set to.
  -v    [verbose] Display OS, loader, chyves guest version, net interfaces,
        and console port.
  -s    [status] Display status of VMM , bhyve instance, and the rcboot value.
  -t    [template] Displays if guest is a template.
  -c    [clone] Displays clone relationship, if any.
  -u    [uefi] Print UEFI console output type, firmware, VNC IP and port, mouse
        type, pause boot until VNC connect, and VNC resolution.
  -d    [description] Displays description property.
  -n    [notes] Dispays notes property.
  -a    [all] Display all fields above.
  -k    [no-disks] Do not display guests' disks.
  -h    [help] Display this message.
  -w    [truncate-to-width] Truncate output to the width of the terminal
  -l    [less] Pipes the output to less.

EOT
		exit
	fi

	# Exit if not guests are available.
	[ "$_NUMBER_OF_ALL_GUESTS" -eq 0 ] && echo "No Guests" && exit

	if [ -z "$( echo "$flags" | grep 'k' )" ]; then
		local _first_col_title="Guest/Disks"
	else
		local _first_col_title="Guest"
	fi

	( # Begining of very large table of output.
	# Print common header
	printf "%s^%s^%s^%s^%s" "$_first_col_title"

	# Print headers for -z flag
	[ -n "$( echo "$flags" | grep -E 'z|a' )" ] && printf "^%s"  "Size"

	# Print headers for -b flag
	[ -n "$( echo "$flags" | grep -E 'b|a' )" ] && printf "^%s^%s" "CPU" "RAM"

	# Print headers for -p flag
	[ -n "$( echo "$flags" | grep -E 'p|a' )" ] && printf "^%s" "Pool"

	# Print headers for -r flag
	[ -n "$( echo "$flags" | grep -E 'r|a' )" ] && printf "^%s" "Dataset Role"

	# Print headers for -v flag
	[ -n "$( echo "$flags" | grep -E 'v|a' )" ] && printf "^%s^%s^%s^%s^%s" "OS" "Loader" "CGV ($_VERSION_CHYVES_GUEST)" "net-ifaces" "Serial"

	# Print headers for -s flag
	[ -n "$( echo "$flags" | grep -E 's|a' )" ] && printf "^%s^%s^%s" "VMM" "Running" "rcboot"

	# Print headers for -t flag
	[ -n "$( echo "$flags" | grep -E 't|a' )" ] && printf "^%s" "Template"

	# Print headers for -c flag
	[ -n "$( echo "$flags" | grep -E 'c|a' )" ] && printf "^%s" "Clone"

	# Print headers for -u flag
	[ -n "$( echo "$flags" | grep -E 'u|a' )" ] && printf "^%s^%s^%s^%s^%s^%s" "UEFI Console" "UEFI Firmware" "UEFI IP:Port" "UEFI Mouse" "UEFI Pause" "UEFI Resolution"

	# Print headers for -d flag
	[ -n "$( echo "$flags" | grep -E 'd|a' )" ] && printf "^%s" "Description"

	# Print headers for -n flag
	[ -n "$( echo "$flags" | grep -E 'n|a' )" ] && printf "^%s" "Notes"

	# Print new line for first guest.
	printf "\n"

	# Spider through pools
	for pool in $_pools; do
		local _dataset_role=$( __return_property_value_from_config_file "manual" "dataset_role" "/chyves/$pool/.config/pool.cfg" )

		if [ -z "$_gst" ]; then
			local _guest_list="$( __return_new_line_delimit_as_space_string $( __return_guest_list $pool ) )"
		else
			local _guest_list="$_gst"
		fi

		# Spider through guests
		for _guest in $_guest_list
		do
			__load_guest_parameters $_guest
			local _size="$( zfs get -H -o value used $_GUEST_pool/chyves/guests/$_GUEST_name )"

			# Print guest name
			printf "%s" "$_GUEST_name"

			# Print size
			[ -n "$( echo "$flags" | grep -E 'z|a' )" ] && printf "^%s" "$_size"

			# Print basic
			[ -n "$( echo "$flags" | grep -E 'b|a' )" ] && printf "^%s^%s" "$_GP_cpu" "$_GP_ram"

			# Print pool
			[ -n "$( echo "$flags" | grep -E 'p|a' )" ] && printf "^%s" "$_GUEST_pool"

			# Print dataset role section
			[ -n "$( echo "$flags" | grep -E 'r|a' )" ] && printf "^%s" "$_dataset_role"

			# Get variable and print for verbose section (os, loader, tap, con)
			if [ -n "$( echo "$flags" | grep -E 'v|a' )" ]; then
				__get_guest_console_pid -h
				printf "^%s^%s^%s^%s^%s" "$_GP_os" "$_GP_loader" "$_GP_chyves_guest_version" "$_GP_net_ifaces" "$_GP_serial_pid"
			fi

			# Get information, set variables, and print for status section (VMM, Running, rcboot)
			if [ -n "$( echo "$flags" | grep -E 's|a' )" ]; then
				local _vmm_status="$( __return_guest_vmm_allocated -human-readable  )"
				local _bhyve_status="$( __return_guest_bhyve_pid -human-readable )"
				local _rcboot_priority="$( __return_guest_rcboot_priority -human-readable )"

				printf "^%s^%s^%s" "$_vmm_status" "$_bhyve_status" "$_rcboot_priority"
			fi

			# Get template information
			if [ -n "$( echo "$flags" | grep -E 't|a' )" ]; then
				if [ "$_GP_template" = "yes" ]; then
					_template="YES"
				else
					_template="NO"
				fi

				printf "^%s" "$_template"
			fi

			# Get clone information to report for guest level, based on first disk. (Yes this is flawed.)
			if [ -n "$( echo "$flags" | grep -E 'c|a' )" ]; then
				__gvset_first_disk
				__gvset_disk_clone_origin_for_info_func $_FIRST_disk
				printf "^%s" "$_DISK_clone_origin_for_info_func"
			fi

			# Print UEFI variables if flag set
			if [ -n "$( echo "$flags" | grep -E 'u|a' )" ]; then
				if [ -n "$_GP_uefi_vnc_ip" ] && [ -n "$_GP_uefi_vnc_ip" ]; then
					local _uefi_ip_and_port="$_GP_uefi_vnc_ip:$_GP_uefi_vnc_port"
				else
					local _uefi_ip_and_port="" # Required due to multiple guests.
				fi
				printf "^%s ^%s ^%s ^%s ^%s ^%s " "$_GP_uefi_console_output" "$_GP_uefi_firmware" "$_uefi_ip_and_port" "$_GP_uefi_vnc_mouse_type" "$_GP_uefi_vnc_pause_until_client_connect" "$_GP_uefi_vnc_res"
			fi

			# Print description if flag set
			[ -n "$( echo "$flags" | grep -E 'd|a' )" ] && printf "^%s" "$_GP_description"

			# Print notes property if flag set
			[ -n "$( echo "$flags" | grep -E 'n|a' )" ] && printf "^%s" "$_GP_notes"

			# Print new line for next guest/disk.
			printf "\n"

			# Do not print disks when the -k flag is used
			if [ -z "$( echo "$flags" | grep 'k' )" ]; then

				# Set $_GUEST_disk_list in space delimited format for "for" loop below.
				__gvset_guest_disk_list -space

				# Iterate over each disk
				for _disk in `echo $_GUEST_disk_list`
				do

					# Handling for raw images files - images named 'disk{n}' will cause issues.
					if [ -z "$( echo "$_disk" | grep -E 'disk[0-9]{1,}$' )" ]; then
						[ -n "$( echo "$flags" | grep -E 'z|a' )" ] && local _size="$( ls -hal $_disk | awk '{ print $5 }' )"
						local _disk_name="$( echo $_disk | cut -d'/' -f7- )"

					# Handling for traditional ZFS volume backed disks
					else
						local _disk_name="$( echo $_disk | cut -d'/' -f8- )"
						[ -n "$( echo "$flags" | grep -E 'z|a' )" ] && local _size="$( zfs get -H -t volume -o value volsize $_GUEST_pool/chyves/guests/$_GUEST_name/$_disk_name )"
					fi

					# Print disk name
					printf "  %s" "$_disk_name"

					# Print qualites (Size, CPU, RAM)
					[ -n "$( echo "$flags" | grep -E 'z|a' )" ] && printf "^%s" "$_size"

					# Do no print dashes unless description or notes flags being used.
					if [ -n "$( echo "$flags" | grep -E 'd|n|a' )" ]; then

						# Print "-" for the rest of the qualites fields (Size, CPU, RAM)
						[ -n "$( echo "$flags" | grep -E 'b|a' )" ] && printf "^-^-"

						# Print "-" for pool
						[ -n "$( echo "$flags" | grep -E 'p|a' )" ] && printf "^-"

						# Print "-" for dataset role
						[ -n "$( echo "$flags" | grep -E 'r|a' )" ] && printf "^-"

						# Print "-" for OS, Loader, CGV, net-ifaces, serial
						[ -n "$( echo "$flags" | grep -E 'v|a' )" ] && printf "^-^-^-^-^-"

						# Print "-" for VMM, Running, rcboot
						[ -n "$( echo "$flags" | grep -E 's|a' )" ] && printf "^-^-^-"

						# Print "-" for Template
						[ -n "$( echo "$flags" | grep -E 't|a' )" ] && printf "^-"

						# Print "-" for Clone
						if [ -n "$( echo "$flags" | grep -E 'c|a' )" ]; then
							__gvset_disk_clone_origin_for_info_func "$_disk"
							printf "^%s" "$_DISK_clone_origin_for_info_func"
							_DISK_clone_origin_for_info_func=
							#printf "^%s" "$_disk"
						fi

						# Print "-" for UEFI properties.
						[ -n "$( echo "$flags" | grep -E 'u|a' )" ] && printf "^-^-^-^-^-^-"

						# Print Description (does not inherit from guest)
						if [ -n "$( echo "$flags" | grep -E 'd|a' )" ]; then
							local _disk_desc="$( __return_property_value_from_config_file "guest" "${_disk_name}_description" )"  # Indirect variable references not supported by /bin/sh otherwise this could be pulled from the already loaded _GP_ variable.
							[ -z "$_disk_desc" ] && local _disk_desc=-
							printf "^%s" "$_disk_desc"
						fi

						# Print Notes (does not inherit from guest)
						if [ -n "$( echo "$flags" | grep -E 'n|a' )" ]; then
							local _disk_notes="$( __return_property_value_from_config_file "guest" "${_disk_name}_notes" )"  # Indirect variable references not supported by /bin/sh otherwise this could be pulled from the already loaded _GP_ variable.
							[ -z "$_disk_notes" ] && local _disk_notes=-
							printf "^%s" "$_disk_notes"
						fi
					fi

					# Print new line for next guest/disk.
					printf "\n"
				done
			fi
		done
	done
	) | column -ts^ | \
	(
		if [ -n "$( echo "$flags" | grep 'l' )" ]
		then
			less
		else
			cat
		fi
	) | \
	(
		if [ -n "$( echo "$flags" | grep 'w' )" ]
		then
			local _term_width="$( tput cols )"
			cut -c -$_term_width
		else
			cat
		fi
	)
	[ -z "$flags" ] && echo "No flags used, for more output see available flags with 'chyves info -h'."
}
