#
# Copyright (c) 2005 IBM Corporation
# Copyright (c) 2005 XenSource Ltd.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

dir=$(dirname "$0")
. "$dir/logging.sh"
. "$dir/locking.sh"

VTPMDB="/var/vtpm/vtpm.db"

#In the vtpm-impl file some commands should be defined:
#      vtpm_create, vtpm_setup, vtpm_start, etc. (see below)
if [ -r "$dir/vtpm-impl.alt" ]; then
	. "$dir/vtpm-impl.alt"
elif [ -r "$dir/vtpm-impl" ]; then
	. "$dir/vtpm-impl"
else
	function vtpm_create () {
		true
	}
	function vtpm_setup() {
		true
	}
	function vtpm_start() {
		true
	}
	function vtpm_suspend() {
		true
	}
	function vtpm_resume() {
		true
	}
	function vtpm_delete() {
		true
	}
	function vtpm_migrate() {
		echo "Error: vTPM migration accross machines not implemented."
	}
	function vtpm_migrate_local() {
		echo "Error: local vTPM migration not supported"
	}
	function vtpm_migrate_recover() {
		true
	}
fi


#Find the instance number for the vtpm given the name of the domain
# Parameters
# - vmname : the name of the vm
# Return value
#  Returns '0' if instance number could not be found, otherwise
#  it returns the instance number in the variable 'instance'
function vtpmdb_find_instance () {
	local vmname ret instance
	vmname=$1
	ret=0

	instance=$(cat $VTPMDB |                   \
	          awk -vvmname=$vmname             \
	          '{                               \
	             if ( 1 != index($1,"#")) {    \
	               if ( $1 == vmname ) {       \
	                 print $2;                 \
	                 exit;                     \
	               }                           \
	             }                             \
	           }')
	if [ "$instance" != "" ]; then
		ret=$instance
	fi
	echo "$ret"
}


# Check whether a particular instance number is still available
# returns "0" if it is not available, "1" otherwise.
function vtpmdb_is_free_instancenum () {
	local instance instances avail i
	instance=$1
	avail=1
	#Allowed instance number range: 1-255
	if [ $instance -eq 0 -o $instance -gt 255 ]; then
		avail=0
	else
		instances=$(cat $VTPMDB |                \
		           awk                          \
		           '{                            \
		               if (1 != index($1,"#")) { \
		                 printf("%s ",$2);       \
		               }                         \
		            }')
		for i in $instances; do
			if [ $i -eq $instance ]; then
				avail=0
				break
			fi
		done
	fi
	echo "$avail"
}


# Get an available instance number given the database
# Returns an unused instance number
function vtpmdb_get_free_instancenum () {
	local ctr instances don found
	instances=$(cat $VTPMDB |                \
	           awk                          \
	           '{                            \
	               if (1 != index($1,"#")) { \
	                 printf("%s ",$2);       \
	               }                         \
	            }')
	ctr=1
	don=0
	while [ $don -eq 0 ]; do
		found=0
		for i in $instances; do
			if [ $i -eq $ctr ]; then
				found=1;
				break;
			fi
		done

		if [ $found -eq 0 ]; then
			don=1
			break
		fi
		let ctr=ctr+1
	done
	echo "$ctr"
}


# Add a domain name and instance number to the DB file
function vtpmdb_add_instance () {
	local res vmname inst
	vmname=$1
	inst=$2

	if [ ! -f $VTPMDB ]; then
		echo "#Database for VM to vTPM association" > $VTPMDB
		echo "#1st column: domain name" >> $VTPMDB
		echo "#2nd column: TPM instance number" >> $VTPMDB
	fi
	res=$(vtpmdb_validate_entry $vmname $inst)
	if [ $res -eq 0 ]; then
		echo "$vmname $inst" >> $VTPMDB
	fi
}


#Validate whether an entry is the same as passed to this
#function
function vtpmdb_validate_entry () {
	local res rc vmname inst
	rc=0
	vmname=$1
	inst=$2

	res=$(cat $VTPMDB |            \
	     awk -vvmname=$vmname     \
	          -vinst=$inst         \
	     '{                        \
	         if ( 1 == index($1,"#")) {\
	         } else                \
	         if ( $1 == vmname &&  \
	              $2 == inst) {    \
	            printf("1");       \
	            exit;              \
	         } else                \
	         if ( $1 == vmname ||  \
	              $2 == inst) {    \
	            printf("2");       \
	            exit;              \
	         }                     \
	     }')

	if [ "$res" == "1" ]; then
		rc=1
	elif [ "$res" == "2" ]; then
		rc=2
	fi
	echo "$rc"
}


#Remove an entry from the vTPM database given its domain name
#and instance number
function vtpmdb_remove_entry () {
	local vmname instance VTPMDB_TMP
	vmname=$1
	instance=$2
	VTPMDB_TMP="$VTPMDB".tmp

	$(cat $VTPMDB |            \
	 awk -vvmname=$vmname     \
	 '{                        \
	    if ( $1 != vmname ) {  \
	      print $0;            \
	    }                      \
	 '} > $VTPMDB_TMP)
	if [ -e $VTPMDB_TMP ]; then
		mv -f $VTPMDB_TMP $VTPMDB
		vtpm_delete $instance
	else
		log err "Error creating temporary file '$VTPMDB_TMP'."
	fi
}


# Find the reason for the creation of this device:
# Returns 'resume' or 'create'
function vtpm_get_create_reason () {
	local resume
	resume=$(xenstore_read $XENBUS_PATH/resume)
	if [ "$resume" == "True" ]; then
		echo "resume"
	else
		echo "create"
	fi
}


#Create a vTPM instance
# If no entry in the TPM database is found, the instance is
# created and an entry added to the database.
function vtpm_create_instance () {
	local res instance domname reason uuid
	uuid=$(xenstore_read "$XENBUS_PATH"/uuid)
	reason=$(vtpm_get_create_reason)

	claim_lock vtpmdb

	instance="0"

	if [ "$uuid" != "" ]; then
		instance=$(vtpmdb_find_instance $uuid)
	fi
	if [ "$instance" == "0" ]; then
		domname=$(xenstore_read "$XENBUS_PATH"/domain)
		instance=$(vtpmdb_find_instance $domname)
	fi

	if [ "$instance" == "0" -a "$reason" != "create" ]; then
		release_lock vtpmdb
		return
	fi

	if [ "$instance" == "0" ]; then
		#Try to give the preferred instance to the domain
		instance=$(xenstore_read "$XENBUS_PATH"/pref_instance)
		if [ "$instance" != "" ]; then
			res=$(vtpmdb_is_free_instancenum $instance)
			if [ $res -eq 0 ]; then
				instance=$(vtpmdb_get_free_instancenum)
			fi
		else
			instance=$(vtpmdb_get_free_instancenum)
		fi

		vtpm_create $instance

		if [ $vtpm_fatal_error -eq 0 ]; then
			if [ "$uuid" != "" ]; then
				vtpmdb_add_instance $uuid $instance
			else
				vtpmdb_add_instance $domname $instance
			fi
		fi
	else
		if [ "$reason" == "resume" ]; then
			vtpm_resume $instance
		else
			vtpm_start $instance
		fi
	fi

	release_lock vtpmdb

	xenstore_write $XENBUS_PATH/instance $instance
}


#Remove an instance when a VM is terminating or suspending.
#Since it is assumed that the VM will appear again, the
#entry is kept in the VTPMDB file.
function vtpm_remove_instance () {
	local instance reason domname uuid
	#Stop script execution quietly if path does not exist (anymore)
	xenstore-exists "$XENBUS_PATH"/domain
	uuid=$(xenstore_read "$XENBUS_PATH"/uuid)

	claim_lock vtpmdb

	instance="0"

	if [ "$uuid" != "" ]; then
		instance=$(vtpmdb_find_instance $uuid)
	fi

	if [ "$instance" == "0" ]; then
		domname=$(xenstore_read "$XENBUS_PATH"/domain)
		instance=$(vtpmdb_find_instance $domname)
	fi

	if [ "$instance" != "0" ]; then
		vtpm_suspend $instance
	fi

	release_lock vtpmdb
}


#Remove an entry in the VTPMDB file given the domain's name
#1st parameter: The name of the domain
function vtpm_delete_instance () {
	local instance

	claim_lock vtpmdb

	instance=$(vtpmdb_find_instance $1)
	if [ "$instance" != "0" ]; then
		vtpmdb_remove_entry $1 $instance
	fi

	release_lock vtpmdb
}

# Determine whether the given address is local to this machine
# Return values:
#  "-1" : the given machine name is invalid
#  "0"  : this is not an address of this machine
#  "1"  : this is an address local to this machine
function vtpm_isLocalAddress() {
	local addr res
	addr=$(ping $1 -c 1 |  \
	       awk '{ print substr($3,2,length($3)-2); exit }')
	if [ "$addr" == "" ]; then
		echo "-1"
		return
	fi
	res=$(ifconfig | grep "inet addr" |  \
	     awk -vaddr=$addr               \
	     '{                              \
	        if ( addr == substr($2, 6)) {\
	          print "1";                 \
	        }                            \
	     }'                              \
	    )
	if [ "$res" == "" ]; then
		echo "0"
		return
	fi
	echo "1"
}

# Perform a migration step. This function differentiates between migration
# to the local host or to a remote machine.
# Parameters:
# 1st: destination host to migrate to
# 2nd: name of the domain to migrate
# 3rd: the migration step to perform
function vtpm_migration_step() {
	local res=$(vtpm_isLocalAddress $1)
	if [ "$res" == "0" ]; then
		vtpm_migrate $1 $2 $3
	else
		vtpm_migrate_local
	fi
}

# Recover from migration due to an error. This function differentiates
# between migration to the local host or to a remote machine.
# Parameters:
# 1st: destination host the migration was going to
# 2nd: name of the domain that was to be migrated
# 3rd: the last successful migration step that was done
function vtpm_recover() {
	local res
	res=$(vtpm_isLocalAddress $1)
	if [ "$res" == "0" ]; then
		vtpm_migrate_recover $1 $2 $3
	fi
}


#Determine the domain id given a domain's name.
#1st parameter: name of the domain
#return value: domain id  or -1 if domain id could not be determined
function vtpm_domid_from_name () {
	local id name ids
	ids=$(xenstore-list /local/domain)
	for id in $ids; do
		name=$(xenstore-read /local/domain/$id/name)
		if [ "$name" == "$1" ]; then
			echo "$id"
			return
		fi
	done
	echo "-1"
}

#Determine the virtual TPM's instance number using the domain ID.
#1st parm: domain ID
function vtpm_uuid_by_domid() {
	echo $(xenstore-read /local/domain/0/backend/vtpm/$1/0/uuid)
}


# Determine the vTPM's UUID by the name of the VM
function vtpm_uuid_from_vmname() {
	local domid=$(vtpm_domid_from_name $1)
	if [ "$domid" != "-1" ]; then
		echo $(vtpm_uuid_by_domid $domid)
		return
	fi
	echo ""
}

#Add a virtual TPM instance number and its associated domain name
#to the VTPMDB file and activate usage of this virtual TPM instance
#by writing the instance number into the xenstore
#1st parm: name of virtual machine
#2nd parm: instance of associated virtual TPM
function vtpm_add_and_activate() {
	local domid=$(vtpm_domid_from_name $1)
	local vtpm_uuid=$(vtpm_uuid_from_vmname $1)
	if [ "$vtpm_uuid" != "" -a "$domid" != "-1" ]; then
		vtpmdb_add_instance $vtpm_uuid $2
		xenstore-write backend/vtpm/$domid/0/instance $2
	fi
}
