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

IPADDR_RE="\([[:digit:]]\{1,3\}\.\)\{3\}[[:digit:]]\{1,3\}"

export PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/pkg/sbin:/usr/pkg/bin
XENSTORE=${XENSTORE:-/usr/pkg/bin/xenstore}

CACHEROOT="/var/cache/xenstore"
XENSTORE_UPDATED=0

case `uname -s` in
NetBSD)
	os=netbsd
	sysctl_balloon="machdep.xen.balloon.current"
	;;
FreeBSD)
	os=freebsd
	sysctl_balloon="dev.xen.balloon.current"
	;;
*)
	echo "OS not supported" > /dev/stderr
	exit 1
	;;
esac

# 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=$(xenstore-read "domid")
cache=${CACHEROOT}/unique-domain-id
newval=$(xenstore-read "/local/domain/${domid}/unique-domain-id")
if [ -e $cache ]; then
    oldval=$(cat "$cache")
    if [ "$oldval" != "$newval" ]; then
	# domain changed
	rm -rf ${CACHEROOT}
    fi
fi
mkdir -p $(dirname "$cache")
echo -n "$newval" > "$cache"

xenstore_rm_cached() {
    key="$1"
    cache=/var/cache/xenstore/$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
}

xenstore_list_interfaces_cached() {
    topdir=${CACHEROOT}/attr
    if [ -d $topdir ] ; then
	cd $topdir 
	for dir in * ; do 
	    [ -f $dir/ip ] && echo $dir
	done
    fi
}

# Memory needs to be reported in kB
# NetBSD:
# cat /proc/meminfo
#        total:    used:    free:  shared: buffers: cached:
#Mem:  16616656896 4481732608 12134924288        0 4078817280 4106203136
#Swap: 1073737728        0 1073737728
#MemTotal:  16227204 kB
#MemFree:   11850512 kB
#MemShared:        0 kB
#Buffers:   11850512 kB
#Cached:     4009964 kB
#SwapTotal:  1048572 kB
#SwapFree:   1048572 kB

# FreeBSD:
# Inspired by https://github.com/ocochard/myscripts/blob/master/FreeBSD/freebsd-memory.sh

if [ $MEMORY_MODE -eq 1 ] ; then
	# Update the memory information
	case "$os" in
	netbsd)
		eval $(cat /proc/meminfo | \
		sed -n -e 's/MemTotal\: *\([0-9]*\)[^$]*/memtotal=\1/gp;' \
	        -e 's/MemFree\: *\([0-9]*\)[^$]*/memfree=\1/gp;')
		;;
	freebsd)
		memtotal=`sysctl -n hw.physmem`
		memtotal=$(($memtotal/1024))
		pagesize=`sysctl -n hw.pagesize`
		freepages=`sysctl -n vm.stats.vm.v_free_count`
		memfree=$(($freepages*$pagesize))
		;;
	esac
	xenstore_write_cached "data/meminfo_total" "${memtotal}"
	xenstore_write_cached "data/meminfo_free" "${memfree}"
fi


#NetBSD 7 and earlier:
#
#xennet0: flags=8863<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST> mtu 1500
#	capabilities=2800<TCP4CSUM_Tx,UDP4CSUM_Tx>
#	enabled=0
#	address: 0e:7e:86:20:7b:82
#	inet 192.168.1.109 netmask 0xffffff00 broadcast 192.168.1.255
#	inet alias 10.20.30.40 netmask 0xffffff00 broadcast 10.20.30.255
#	inet6 fe80::c7e:86ff:fe20:7b82%xennet0 prefixlen 64 scopeid 0x1
#lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33184
#	inet 127.0.0.1 netmask 0xff000000
#	inet6 ::1 prefixlen 128
#	inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2

#NetBSD 8 and later:
#
#xennet0: flags=0x8863<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST> mtu 1500
#	capabilities=2800<TCP4CSUM_Tx,UDP4CSUM_Tx>
#	enabled=0ec_capabilities=1<VLAN_MTU>
#	ec_enabled=0
#	address: 5e:a4:a1:fc:eb:70
#	inet 192.168.1.17/24 broadcast 192.168.1.255 flags 0x0
#	inet 10.20.30.40/16 broadcast 10.20.255.255 flags 0x0
#	inet6 fe80::5ca4:a1ff:fefc:eb70%xennet0/64 flags 0x0 scopeid 0x1

#FreeBSD 14:
#xn0: flags=1008843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP> metric 0 mtu 1500
#        options=503<RXCSUM,TXCSUM,TSO4,LRO>
#        ether 96:fe:50:f8:b9:e6
#        inet 192.168.1.81 netmask 0xffffff00 broadcast 192.168.1.255
#        media: Ethernet manual
#        status: active
#        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>

# Original:
#
#/sbin/ifconfig | \
#        sed -n -e '/^[0-9a-z][0-9a-z]*\:/,/^$/d' \
#               -e 's/^\([0-9a-z][0-9a-z]*\) .*/ifs="\$ifs \1"; current="\1"; /gp;' \
#               -e 's/  *inet \('$IPADDR_RE'\) .*/eval inet_\${current}="\1"; /gp;'

eval $(/sbin/ifconfig -a | \
	awk '{
		if ($0~"^[a-z][a-z0-9]*[0-9]:") {
			if ($0~"^xennet[0-9]+:") {
				current = $1
				sub(":.*", "", current)
				sub("xennet", "eth", current)
				iflist = iflist " " current
				ifs[current] = ""
			} else {
				current =""
			}
		}
		if (($1 == "inet" || $1 == "inet6") && current != "") {
			if ($2 == "alias") ip = $3
			else ip = $2
			if ($1 == "inet6") sub("%.*", "", ip)
			sub("/.*", "", ip)
			if (ifs[current] != "") ifs[current] = ifs[current] ", " ip
			else ifs[current] = ip
		}
	}
	END {
		sub("^ ", "", iflist)
		printf "ifs=%c%s%c\n", 39, iflist, 39
		for (i in ifs)
			printf "inet_%s=%c%s%c\n", i, 39, ifs[i], 39
	}')

# network
for if in $ifs ; do

	[ "${if}" = "lo" ] && continue
	inet=$(eval echo \${inet_${if}})
	[ -z "${inet}" ] && continue

	xenstore_write_cached "attr/${if}/ip" "${inet}"
done

# remove any interfaces that have been unplugged or downed
for at in $(xenstore_list_interfaces_cached) ; do
    for if in $ifs ; do
	[ "${if}" = "${at}" ] && continue 2
    done
    xenstore_rm_cached "attr/${at}"
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
sysctl -n $sysctl_balloon > /dev/null 2>&1
if [ "$?" = 0 ]; then
	xenstore_write_cached "control/feature-balloon" "1"
else
	xenstore_write_cached "control/feature-balloon" "0"
fi

# build time addons
xenstore_write_cached "attr/PVAddons/MajorVersion" "7"
xenstore_write_cached "attr/PVAddons/MinorVersion" "0"
xenstore_write_cached "attr/PVAddons/MicroVersion" "0"
xenstore_write_cached "attr/PVAddons/BuildVersion" "125243"
xenstore_write_cached "attr/PVAddons/Installed" "1" 

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

