#!/bin/bash
# Copyright 2010 Novell, Inc.
# Author: Peter Bowen <pzb@novell.com> as a work made for hire.
#
# This work is licensed under the 
# Creative Commons Attribution-ShareAlike 3.0 Unported License. 
# To view a copy of this license, visit 
# http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to 
# Creative Commons, 171 Second Street, Suite 300, 
# San Francisco, California, 94105, USA.
#

export LANG=en_US.UTF-8

DEBUG=0
[ "$1" == "--debug" ] && DEBUG=1

PRIVATE_KEYS="/meta-data/ami-manifest-path /meta-data/public-keys/ /meta-data/security-groups /user-data"

HAVECURL=0
HAVEXENSTORE=0
HAVETRACEROUTE=0
HAVEDMIDECODE=0
UDEVINFO=""
HAVEGETFATTR=0

if [ $(type -p curl) ]; then
    HAVECURL=1
fi

if [    "$(type -p xenstore-read)" \
     -a "$(type -p xenstore-ls)" \
     -a "$(type -p xenstore-list)" ]; then
    HAVEXENSTORE=1
fi

if [ $(type -p traceroute) ]; then
    HAVETRACEROUTE=1
fi

if [ $(type -p dmidecode) ]; then
    HAVEDMIDECODE=1
fi

if [ $(type -p udevadm) ]; then
    UDEVINFO="udevadm info --query=all --name="
elif [ $(type -p udevinfo) ]; then
    UDEVINFO="udevinfo -q all -n "
fi

if [ $(type -p getfattr) ]; then
    HAVEGETFATTR=1
fi

##function: xml-escape
function xml-escape
{
  sed -e 's/&/\&amp;/g;s/</\&lt;/g;s/>/\&gt;/g'
}  

##function: ec2-instance-data
ec2_raw_instance_data() {
  local ver="latest"
  [ "$2" ] && ver="$2"
  curl --retry 3 --retry-delay 0 --silent --fail "http://169.254.169.254/$ver$1"
}

##function: ec2-instance-data
ec2-instance-data() {
  ec2_raw_instance_data "$@"
  [ $? -eq 0 ] && echo
}

##function: check_exclude_data
function check_exclude_data 
{
  local KEY="$1"
  for PREFIX in $PRIVATE_KEYS; do
    TR="${KEY/$PREFIX/##EXCLUDED##}"
    if [ "${TR:0:12}" == "##EXCLUDED##" ]; then
        return 0
    fi
  done
  return 1
}

##function: print_excluded
function print_excluded
{
    local H
    local F
    case "$1" in
    /meta-data/ami-manifest-path)
        MP=$(ec2_raw_instance_data "$1")
        if [ "$MP" == "(unknown)" ]; then
            H="unknown"
        else
            H=$(echo "$MP" | sha1sum | cut -f 1 -d ' ')
        fi
        echo '<instance_data key="'"$1"'" hash="'$H'"/>'
        ;;
    /meta-data/public-keys/*/openssh-key)
        F=$(mktemp)
        ec2_raw_instance_data "$1" > "$F"
        H=$(ssh-keygen -l -f "$F" | cut -d ' ' -f 1,2)
        rm -f "$F"
        echo '<instance_data key="'"$1"'" fingerprint="'$H'"/>'
        ;;
    *)    
        echo '<instance_data key="'"$1"'" excluded="1" />'
        ;;
    esac
}

##function: printid
function printid
{
  check_exclude_data "$1"
  local EXCLUDED=$?
  if [ $EXCLUDED -eq 0 ]; then
    print_excluded "$1"
  else
    D=$(ec2_raw_instance_data "$1")
    [ -z "$D" ] && return 1
    echo -n '<instance_data key="'"$1"'">'
    ec2_raw_instance_data "$1" | xml-escape
    echo '</instance_data>'
  fi
}

##function: key_is_broken
function key_is_broken
{
	KEY="$1"
	A=$(ec2_raw_instance_data "$1" | md5sum)
	B=$(ec2_raw_instance_data "$1/" | md5sum)
	[ "$A" == "$B" ]
}


##function: dump-instance-data
function dump-instance-data
{
    local PKEY="$1"
    if [ -z "$PKEY" ]; then
        PKEY="/"
    fi
    # Strip trailing /
    printed=0
    PKEY=${PKEY%/}
    echo "$PKEY" | grep -F -q =
    if [ $? -eq 0 ]; then
        dump-instance-data "${PKEY%=*}"
    fi
    printid "${PKEY}"
    [ $? -eq 0 ] && printed=1
    key_is_broken "$PKEY" && return 0
    ec2-instance-data "$PKEY/" | {
        while read -r KEY; do
            KEY=${KEY%/}
	    [ -z "$KEY" ] && continue
            dump-instance-data "${PKEY}/${KEY}"
            printed=1
        done
        if [ $printed -ne 1 ]; then
            echo '<instance_data key="'"${PKEY}"'" />'
        fi
    }
}

##function: printxs
function printxs
{
  local value=$(xenstore-read "$1")
  echo -n '<xenstore key="'"$1"'" value="'"$value"'">'
  xenstore-ls "$1" | xml-escape
  echo '</xenstore>'
}

echo '<?xml version="1.0" encoding="UTF-8"?>'
echo '<instanceinfo version="2010-08-29">'

if [ -d /etc/sces/products ]; then 
    find /etc/sces/products -type f | while read FN; do
        echo -n '<product_cert file="'"$FN"'">'
        cat /etc/sces/update.cert | xml-escape
        echo '</product_cert>'
    done
fi

echo -n '<cpuinfo>'
cat /proc/cpuinfo | xml-escape
echo '</cpuinfo>'

if [ $DEBUG -eq 1 ] ; then
    echo -n '<dmesg>'
    dmesg | xml-escape
    echo '</dmesg>'
fi

echo -n '<mounts>'
cat /proc/mounts | xml-escape
echo '</mounts>'

HAVEINSTANCESTORE=0
if [ $HAVECURL -eq 1 ]; then
    # Assume that we will always have "latest" as a pointer to the current version
    # Not using ec2-meta-data or ec2-instance-data because we want a very fast timeout
    curl --silent --fail --connect-timeout 1 http://169.254.169.254/latest/meta-data/ &>/dev/null
    if [ $? -eq 0 ]; then
        HAVEINSTANCESTORE=1
    fi
fi

if [ $HAVEINSTANCESTORE -eq 1 ]; then
    echo -n "<instance_data_versions>"
    curl --retry 3 --retry-delay 0 --silent --fail "http://169.254.169.254/" | xml-escape
    echo "</instance_data_versions>"

    dump-instance-data
fi

if [ $HAVEXENSTORE -eq 1 -a -f /proc/xen/xenbus ]; then
    DOMID=$(xenstore-read domid)
    if [ "$DOMID" ]; then
      printxs "/local/domain/$DOMID" 
      xenstore-ls "/local/domain/$DOMID" | \
        fgrep /local/domain/0/backend | \
        sed -r -e 's@.*(/local/domain/0/backend/.*)"@\1@' | \
        while read KEY; do
          printxs "$KEY"
      done
    fi
fi

if [ $HAVETRACEROUTE -eq 1 ]; then
  echo -n "<traceroute>"
  traceroute -m 2 s3.amazonaws.com | tail -n +2 | xml-escape | \
    sed -e 's/.*(/<hop>/;s@).*@</hop>@'
  echo "</traceroute>"
fi

if [ -f /var/lib/dhcpcd/dhcpcd-eth0.info ]; then
    echo -n "<dhcpcd_info interface=\"eth0\">"
    cat /var/lib/dhcpcd/dhcpcd-eth0.info
    echo "</dhcpcd_info>"
fi 

if [ -e /sys/hypervisor ]; then
  find /sys/hypervisor ! -type d| while read FN; do
   echo -n '<sysfs name="'"$FN"'">'
   cat "$FN"| xml-escape
   echo '</sysfs>'
  done
else
  if [ $HAVEDMIDECODE -eq 1 ]; then
    for S in bios-vendor bios-version bios-release-date \
             system-product-name system-uuid; do
      echo -n '<dmi string="'"$S"'">'
      dmidecode -s "$S" | xml-escape
      echo '</dmi>'
    done
  fi
fi

find /etc -maxdepth 1 -name '*-release' | while read FN; do
  echo -n '<file name="'"$FN"'">'
  cat "$FN" | xml-escape
  echo '</file>'
done

# SLES11
if [ -d /etc/products.d ]; then
  find /etc/products.d -name '*.prod' | while read FN; do
    echo -n '<product filename="'"$FN"'">'
    cat "$FN" | xml-escape
    echo '</product>'
  done
fi

# SLES10
if [ -d /var/lib/zypp/db/products ]; then
  find /var/lib/zypp/db/products -type f | while read FN; do
    echo -n '<product filename="'"$FN"'">'
    cat "$FN" | xml-escape
    echo '</product>'
  done
fi

if [ -e /etc/products.d/baseproduct ]; then
    bp=$(readlink /etc/products.d/baseproduct)
    echo '<baseproduct name="'"$bp"'"/>'
fi

if [ $HAVEGETFATTR -eq 1 ]; then
    echo -n '<fattr path="/">'
    getfattr --absolute-names -d -m - / | egrep -v '^#' | xml-escape
    echo '</fattr>'
fi

echo -n '<uname>' 
for F in s r v m p i o; do
  echo -n "<$F>"
  uname -$F | xml-escape
  echo "</$F>"
done
echo '</uname>'

if [ -d /sys/class/block ]; then
    ls /sys/class/block | while read DEV; do
        echo -n '<block dev="'"$DEV"'">'
        [ "$UDEVINFO" ] && $UDEVINFO"$DEV" 2>/dev/null | xml-escape
        echo '</block>'
    done
elif [ -d /sys/block ]; then
    ls /sys/block | while read DEV; do
        echo -n '<block dev="'"$DEV"'">'
        [ "$UDEVINFO" ] && $UDEVINFO"$DEV" 2>/dev/null | xml-escape
        echo '</block>'
    done
fi

echo '</instanceinfo>'
