#!/bin/sh

# Copyright (C) 2009  Citrix Systems Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# Script to write information about the guest to XenStore.
#
# Information collected (if --memory NOT passed in):
#   - Distribution name
#   - Distribution version (major and minor)
#   - Kernel version (uname)
#   - IP address for each Ethernet interface
#
# Information collected (if --memory IS passed in):
#   - memtotal
#   - memfree
#
# Memory stats are separated out because they change all the time
# and so we may not want to update them as frequently

LANG="C"
export LANG


XE_LINUX_DISTRIBUTION_CACHE=/var/cache/xe-linux-distribution

export PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/sbin
XENSTORE=${XENSTORE:-/usr/local/bin/xenstore}
CACHEROOT="/var/cache/xenstore"
XENSTORE_UPDATED=0

# parse command line opts

MEMORY_MODE=0 # do not update memory stats 
while [ $# -ge 1 ] ; do 
    if [ "$1" = "--memory" ] ; then
	MEMORY_MODE=1	# update only memory stats 
    fi
    shift
done

xenstore_write_cached() {
    key="$1" newval="$2"
    cache=${CACHEROOT}/$key
    if [ -f $cache ] ; then
	# cache exists
	oldval=$(cat "$cache")
	if [ "$oldval" = "$newval" ] ; then
	    # value unchanged
	    return 0
	fi
    else
	# cache does not exist
	if [ -e $cache ] ; then 
	    # something (directory?) in its way
	    rm -rf $cache
	fi
    fi
    
    # try to write and update cache if successfull
    if $XENSTORE write "$key" "$newval" ; then
	mkdir -p $(dirname "$cache")
	echo -n "$newval" > "$cache"
	XENSTORE_UPDATED=1
	return 0
    fi
    return 1
}

# If we detect a domain change then delete our cache and force a refresh
domid=$(/usr/local/bin/xenstore-read "domid")
cache=${CACHEROOT}/unique-domain-id
newval=$(/usr/local/bin/xenstore-read "/local/domain/${domid}/unique-domain-id")
if [ -e $cache ]; then
    oldval=$(cat "$cache")
    if [ "$oldval" != "$newval" ]; then
	# domain changed
	rm -rf xenstore
    fi
fi
mkdir -p $(dirname "$cache")
echo -n "$newval" > "$cache"

xenstore_rm_cached() {
    key="$1"
    cache=${CACHEROOT}/$key
    if [ ! -e $cache ] ; then
	return 1
    fi
    # try to write and update cache if successfull
    if $XENSTORE rm "$key" ; then
	rm -rf "$cache"
	XENSTORE_UPDATED=1
	return 0
    fi
    return 1
}

# read cache, using xen-style integers (e.g., "0", "1")
# return bsd names like xn0
xenstore_list_interfaces_cached() {
    topdir=${CACHEROOT}/attr
    if [ -d $topdir ] ; then
	cd $topdir 
	# if a file named like vif/0/ipv4/0 exists, then xn0 is a valid interface
	# sort -u to get only unique interfaces
	iflist=$(ls vif/*/ipv?/* | cut -d / -f 2 | sort -u | xargs echo)
	for n in $iflist
	do
		echo "xn${n}"
	done
    fi
}

# XAPI expects things like attr/vif/0/ipv4/0 = 172.30.0.2
# So this tries to map BSD names like xn0 to attr/vif/0
xe_ip_if() {
        interfaces=$(/sbin/ifconfig -u | grep ': flags=' | grep xn | cut -d ':' -f1)
	n=0
        for ifacename in $interfaces ; do
                v4ips=$(/sbin/ifconfig $ifacename | grep 'inet ' | cut -d ' ' -f2)
		# FreeBSD reports some IPv6 addrs like fe80:1234::abcd%xn0, the `cut` strips that off
                v6ips=$(/sbin/ifconfig $ifacename | grep 'inet6 ' | cut -d ' ' -f2 | cut -d % -f 1 )
		i=0
		for v4ip in $v4ips
		do
			echo "vif/$n/ipv4/$i | $v4ip"
			i=$(($i+1))
		done

		i=0
		for v6ip in $v6ips
		do
			echo "vif/$n/ipv6/$i | $v6ip"
			i=$(($i+1))
		done
		n=$(($n+1))
        done
}

if [ $MEMORY_MODE -eq 1 ] ; then
	# calc memory... used http://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt as guide
	pagesize=$(sysctl hw.pagesize | cut -d ':' -f2)
	memtotal=$(let "$(sysctl hw.physmem | cut -d ':' -f2) / 1024")
	meminactive=$(let "$(sysctl vm.stats.vm.v_inactive_count | cut -d ':' -f2) * $pagesize / 1024")
	memcache=$(let "$(sysctl vm.stats.vm.v_cache_count | cut -d ':' -f2) * $pagesize / 1024")
	memfree=$(let "$(sysctl vm.stats.vm.v_free_count | cut -d ':' -f2) * $pagesize / 1024")
	memavail=$(let "$meminactive + $memcache + $memfree")

	# we're using memavail as "free" for now
	xenstore_write_cached "data/meminfo_total" "$memtotal"
	xenstore_write_cached "data/meminfo_free" "$memavail"
fi

# xe_ip_if | while read linea
# do
#   if=$(echo $linea | cut -d '|' -f1 | sed 's/ //g')
#   inet=$(echo $linea | cut -d '|' -f2 | sed 's/^ //')
#   xenstore_write_cached "attr/${if}/ip" "${inet}" 
# done

# This breaks apart a string like "vif/0/ipv4/0 | 172.16.0.8" into 2 arguments for
# xenstore_write_cached
xe_ip_if | while read linea
do
  if=$(echo $linea | cut -d '|' -f1 | tr -d ' ' )
  inet=$(echo $linea | cut -d '|' -f2 | tr -d ' ' )
  xenstore_write_cached "attr/${if}" "${inet}" 
done

# remove any interfaces that have been unplugged or downed
for xn in $(xenstore_list_interfaces_cached) ; do
	link=1
	iface=$(/sbin/ifconfig $xn | grep "UP," | cut -d ':' -f1)
	[ "${iface}" = "${xn}" ] && link=0
	
	if [ "$link" -gt "0" ] ; then
		# bsd-named interface is gone, remove attr from xenstore
		vifnum=$(echo $xn | sed 's/xn//g')
		echo "removing vif $vifnum"
		xenstore_rm_cached "attr/vif/${vifnum}"
	fi
done

# distro
if [ -f ${XE_LINUX_DISTRIBUTION_CACHE} ] ; then
    . ${XE_LINUX_DISTRIBUTION_CACHE}
    for key in os_name os_majorver os_minorver os_uname os_distro ; do
	new=$(eval echo \${${key}})
	[ -n "${new}" ] || continue
	xenstore_write_cached "data/${key}" "${new}"
    done
fi

# whether I support ballooning or not
xenstore_write_cached "control/feature-balloon" "1"

# build time addons
xenstore_write_cached "attr/PVAddons/MajorVersion" "6"
xenstore_write_cached "attr/PVAddons/MinorVersion" "2"
xenstore_write_cached "attr/PVAddons/MicroVersion" "1" 
xenstore_write_cached "attr/PVAddons/BuildVersion" "76888"
xenstore_write_cached "attr/PVAddons/Installed" "1" 

# update xenstore if necc
if [ $XENSTORE_UPDATED -eq 1 ] ; then
    xenstore_write_cached "data/updated" "$(date)"
fi
