#!/bin/bash
#
#   Copyright 2005-2007 VMware, Inc.  All rights reserved.
#
#   Support program for VMware products on Linux hosts.
#
#   Collects various configuration and log files
#   for use when troubleshooting VMware products on Linux hosts.
#


#   Function: usage prints how to use this script

function usage {
   echo ""
   echo "Usage: $0 [-h] [-w output directory] [-q]"
   echo "  -h prints this usage statement"
   echo "  -q runs in quiet mode"
   echo "  -w <dir> sets the working directory used for the output files"
   exit
}


TARFILE=vm-$(date -I).$$.tgz
VER=0.87
OUTPUT_DIR=vm-support.$$

#   Function: banner prints any number of strings padded with 
#   newlines before and after.

function banner {
   echo
   for option in "$@"
   do
      echo $option
   done
   echo
}


function UpdateSpinner {
   case $SPINNER in
      "|")
           SPINNER="/"
      ;;

      "/")
           SPINNER="-"
      ;;

      "-")
           SPINNER="\\"
      ;;

      "\\")
           SPINNER="|"
      ;;

      *)
           SPINNER="|"
      ;;
   esac
   echo -en "\rPreparing Files: $SPINNER"
}

#   Function: addtar copies whatever files and directories you give it to 
#   a self contained output directory for later tar'ing
#   Working on copies could slow this down with VERY large files but:
#   1) We don't expect VERY large files
#   2) Since /proc files can be copied this preserves the tree without
#      having to cat them all into a file.
#   3) tar barfs on open files like logs if it changes while it's tar'ing.
#     Copying file first makes sure tar doesn't complain


function addtar {
   FILE=$1

   DIR=$(dirname "$FILE")
   if [ ! -d "${OUTPUT_DIR}$DIR" ]; then
      mkdir -p "${OUTPUT_DIR}$DIR"

      if [ $? != 0 ]; then
         banner "Could not create ./${OUTPUT_DIR}$DIR... " \
                "Have you run out of disk space?" "Continuing"
         return
      fi
   fi

   # Ignore stdout and handle errors.
   if [ $quiet_mode -eq 0 ]; then
      UpdateSpinner
   fi
   cp -pr "$FILE" "${OUTPUT_DIR}$DIR" 2>/dev/null

   # We could have failed to copy for several reasons
   # If we path had a shell special character (* ? .)
   # or if the file is in /proc
   if [ $? != 0 ]; then
      FILENAME=${FILE##*/}
      for line in "$DIR"/$FILENAME
      do
         if [ -e "$line" ]; then
            # Ignore stdout and handle errors.
            if [ $quiet_mode -eq 0 ]; then
               UpdateSpinner
            fi
            cp -pr "$line" "${OUTPUT_DIR}$DIR" 2>/dev/null

            # If a file from /proc does not copy,
            # ignore - they're funny.  
            # Otherwise, exit for failed copy.
            if [ $? != 0 ]; then
               echo "$line" | grep ^/proc > /dev/null

               if [ $? != 0 ]; then
                  banner "Could not copy $line \
                          to the tar area."
                  return
               fi   # Not proc
            fi # is it proc
         fi # does file exist

      done # for each file in the list
   fi # if copy failed

}


#   Function: runcmd executes the command ($1) 
#   redirected to a file ($2) and then adds that 
#   file to the list of files to tar. 
#   It then deletes the temp file since addtar makes a copy in its own
#   selft contained area.

function runcmd {
   $1 > $2 2>/dev/null
   
   if [ $? != 0 ]; then
      banner "Either could not run $1 or could not write to $2" \
             "Do you have a full disk?" "Continuing..."
   else
      addtar "$2"
      rm "$2"

      if [ $? != 0 ]; then
         banner "Could not delete $2.  Continuing..." 
      fi
   fi
}

function addteam {
   teamfile=$1

   sed -n -e "s/<URL type=\"string\">//Ip" "$line" | \
      sed -n -e "s/<\/URL>//Ip" | while read vmline
   do
      addvm "$vmline"
   done
}

function addvm {                        
   cfgfile=${1//\"/}                 

   # No config file... nothing to do here.
   if ! [ -e "$cfgfile" ]; then
      return
   fi

   # See if this is a team file
   suffix=${cfgfile##*.}
   if [ "$suffix" = "vmtm" ];then
      addteam "$cfgfile"
   fi

   [ -e "$OUTPUT_DIR$cfgfile" ] && return    
   addtar "$cfgfile"
   dir=$(dirname "$cfgfile")              
   if [ $collect_state_logs -eq 1 ]
   then
      addtar "$dir/*.log"
   else
      addtar "`ls "$dir"/*.log | grep -v state`"
   fi
   addtar "$dir/*.vmx"
   addtar "$dir/*.vmxf"
   addtar "$dir/*.vmtm"
   addtar "$dir/*.vmpl"
   addtar "$dir/vmware-core"
   addtar "$dir/vmware-core*.gz"
   addtar "$dir/core"
   addtar "$dir/core.*"
   # monitor stats
   addtar "$dir/vmware-stats*"
   addtar "$dir/stats32*"
   addtar "$dir/stats64*"
   # for gprof
   addtar "$dir/gmon.monitor*"
   # for callstack
   addtar "$dir/callstacks.vcpu*"
   addtar "$dir/samples.vcpu*"
   addtar "$dir/status"

}

#
# Get support information about hostd
#

function snarf_hostd_state {
   #   Parse /etc/vmware/hostd/vmInventory.xml and add the virtual 
   #   machine configuration and log files to the 
   #   list of files

   INVENTORY_LOCATION="/etc/vmware/hostd/vmInventory.xml"
   NEW_LOCATION=`sed -n -e "s/^\s*<vmInventory>\s*\(\.*\)/\1/pg" /etc/vmware/hostd/config.xml | sed -n -e "s/\s*<\/vmInventory>//pg"`
   if [ -n "$NEW_LOCATION" ]; then
      INVENTORY_LOCATION=$NEW_LOCATION
   fi
   # Grab the inventory file itself, in case it is configured differently
   addtar $INVENTORY_LOCATION
   CFGLIST=`sed -n -e "s/^\s*<vmxCfgPath>\s*\(\.*\)/\1/g" -e "s/<\/vmxCfgPath>//pg" $INVENTORY_LOCATION`
   IFS=$'\n'
   for CFGNAME in $CFGLIST; do
      addvm "$CFGNAME"
   done
   unset IFS

   # Grab a dump of all hostd properties export through the VIM API.
   # XXX: Cant do this outside ESX because we cant assume the python environment is
   # setup correctly in non-ESX products. See bug 119163
   # runcmd "/usr/bin/vmware-vimdump" "/tmp/vimdump.$$.txt"
}

#
# Collect information about ACE Management Server.
#

function collect_ACE_server_logs {
   addtar "/var/lib/vmware/acesc/conf"
   addtar "/var/lib/vmware/acesc/bin/acesc-config.conf"
   addtar "/var/lib/vmware/acesc/log"
   if [ -e /etc/httpd ]; then
      addtar "/etc/httpd/conf/httpd.conf"
      addtar "/etc/httpd/conf.d"
      addtar "/var/log/httpd/logs"
   fi
   if [ -e /etc/apache2 ]; then 
      addtar "/var/lib/apache2"
      addtar "/etc/apache2/*.conf"
   fi
}


quiet_mode=0
working_dir=
collect_state_logs=0

# main()
while getopts hqsw: arg ; do
   case "$arg" in
      "q") quiet_mode=1 ;;
      "w") working_dir=$OPTARG ;;
      "s") collect_state_logs=1 ;;
      "h") usage ;;
        *) usage ;;
   esac
done

#   Start message

banner "Support program for VMware products on Linux hosts - Version $VER"

#   Check for root privledge
   
if [ $(id -u) != "0" ]; then            
   banner "You are not root, some system information can't be collected."
fi

# Source /etc/profile.  If we can't find it, it's the users problem to get
# their paths straight.

if [ -f /etc/profile ]; then
   . /etc/profile
fi

# Protect against non-default values of $IFS (Not all scripts in /etc/profile.d/ 
# are good citizens).
unset IFS

#       Set the working directory if specified

if [ -n "$working_dir" ]; then
   if ! cd "$working_dir"; then
      banner "Could not set working directory to '$working_dir'."
      exit
   fi
fi

# In quiet mode, print this here since the spinner won't be printed/updated
if [ $quiet_mode -eq 1 ]; then
    banner "Preparing files ..."
fi

#   make a subdir to put all your files in.  die if it does not create
mkdir $OUTPUT_DIR

if [ $? != 0 ]; then
   banner "Could not create ./${OUTPUT_DIR}... Exiting..." \
          "Please cd to a directory to which you can write" 
   exit
fi

#   Parse /etc/vmware/vm-list and add the virtual
#   machine configuration and log files to the
#   list of files

if [ -e ~/.vmware/favorites.vmls ]; then
   # This picks up only the user registered VM's
   sed -n -e "s/.*config *= \"//Ip" ~/.vmware/favorites.vmls | \
      sed -n -e "s/\"//Ip" | \
      while read line
   do
      addvm "$line"
   done
fi

#       Pick up the user's vmlist for GSX installs. 

if [ -e ~/.vmware/vmlist ]; then
   # This picks up only the user registered VM's
   sed -n -e "s/.*config *= \"//Ip" ~/.vmware/vmlist | \
      sed -n -e "s/\"//Ip" | \
      while read line
   do
      addvm "$line"
   done
fi

if [ $(id -u) == "0" ] && [ -e "/etc/vmware/vm-list" ]; then            
   # This picks up the VM in /etc/vmware/vm-list
   sed -n -e "s/config \"//Ip" /etc/vmware/vm-list | \
      sed -n -e "s/\"//Ip" | \
      while read line
   do
      addvm "$line"
   done 

fi

if [ -e ~/.vmware/preferences ]; then
    sed -n -e "s/.*openedObj.\+\.file *= \"//Ip" ~/.vmware/preferences | \
    sed -n -e "s/\"//Ip" | \
    while read line
    do
      addvm "$line"
    done
fi

# Try to collect bootloader config
if [ -e /etc/lilo.conf ]; then
   addtar "/etc/lilo.conf"
fi

# And for grub we are not sure about the exact default location so collect them
# all.
if [ -e /boot/grub/grub.conf ]; then
   addtar "/boot/grub/grub.conf"
fi
if [ -e /boot/grub/menu.lst ]; then
   addtar "/boot/grub/menu.lst"
fi
if [ -e /etc/grub.conf ]; then
   addtar "/etc/grub.conf"
fi

# Add virtual machines spotted from hostd if present
if [ -e /etc/vmware/hostd/config.xml ]; then
   snarf_hostd_state
fi

if [ -e /var/lib/vmware/acesc ]; then
   collect_ACE_server_logs
fi

addtar "/etc/crontab"
addtar "/etc/cron.daily"
addtar "/etc/cron.hourly"
addtar "/etc/cron.monthly"
addtar "/etc/cron.weekly"
addtar "/etc/modules.conf"
addtar "/etc/ntp.conf"
addtar "/etc/vmware/*"
addtar "/etc/security/*"

# Add UI logs
addtar "/tmp/vmware*"

# Add services
addtar "/etc/services"

HOMEDIR=`readlink -f -n ~/.vmware`
addtar "$HOMEDIR/*"
addtar "/var/log/boot*"
addtar "/var/log/secure*"
addtar "/var/log/messages*"
addtar "/etc/xinetd.d/vmware-authd"
addtar "/var/log/vmware/*"
addtar "/var/lib/vmware/hostd/*"

# Add /proc with some exceptions.  stdout redirected to /dev/null.  Some files
# come and go and confuse find.  Just send whatever works and don't scare user.

for procfile in `find /proc -type f 2>/dev/null| egrep -v kcore\|kmsg\|acpi\|/proc/$$`
do
   addtar "$procfile"
done

#   Commands to run ($1) and redirect to logs ($2) for 
#   inclusion. 

runcmd "echo vm-support version: $VER" "/tmp/vm-support-version.$$.txt"
runcmd "lspci -H1 -M" "/tmp/lspci1.$$.txt"
runcmd "lspci -H1 -M -vn" "/tmp/lspci2.$$.txt"
runcmd "/sbin/lsmod" "/tmp/modules.$$.txt"
runcmd "uname -a" "/tmp/uname.$$.txt"
runcmd "df" "/tmp/df.$$.txt"
runcmd "cat /etc/issue" "/tmp/issue.$$.txt"
runcmd "ifconfig -a" "/tmp/ifconfig.$$.txt"
# Dont assume rpm exists (debian/ubuntu dont provide it)
if [ -e `which rpm` ]; then
   runcmd "rpm -qa" "/tmp/rpm-qa.$$.txt"
fi
runcmd "netstat -lan" "/tmp/netstat-lan.$$.txt"
runcmd "route" "/tmp/route.$$.txt"
runcmd "mount" "/tmp/mount.$$.txt"
runcmd "vmware -v" "/tmp/vmware.$$.txt"
runcmd "dmesg" "/tmp/dmesg.$$.txt"
runcmd "free" "/tmp/free.$$.txt"
runcmd "uptime" "/tmp/uptime.$$.txt"
runcmd "date" "/tmp/date.$$.txt"
runcmd "ps auwwx" "/tmp/ps-auwwx.$$.txt"
runcmd "ulimit -a" "/tmp/ulimit-a.$$.txt"
runcmd "umask" "/tmp/umask.$$.txt"

#   Terminate the spinner
echo
echo

#   Perform the tar ('S' for sparse core files)

if [ $quiet_mode -eq 0 ]; then
   tar -czSvf $TARFILE $OUTPUT_DIR
else
   tar -czSf $TARFILE $OUTPUT_DIR
fi

if [ $? != 0 ]; then
   banner "The tar did not successfully complete!" \
          "If tar reports that a file changed while reading, please attempt to rerun this script."
else
   size=`stat -t $TARFILE | cut -f2 -d " "`
   if [ $size -gt 10000000 ]; then
      banner "File: $(pwd | sed s/[\/]$//)/$TARFILE" \
             "NOTE: $TARFILE is greater than 10 MB." \
             "Please do not attach this file when submitting an incident report." \
             "Please contact VMware support for an ftp site." \
             "To file a support incident, go to http://www.vmware.com/support/sr/sr_login.jsp"
   else
      banner "File: $(pwd | sed s/[\/]$//)/$TARFILE" \
             "Please attach this file when submitting an incident report." \
             "To file a support incident, go to http://www.vmware.com/support/sr/sr_login.jsp"
   fi
fi


#   Clean up temporary files

rm -rf $OUTPUT_DIR

if [ $? != 0 ]; then
   banner "$OUTPUT_DIR was not successfully removed.  Please remove manually."
fi

#   End
