#!/usr/bin/env bash
#
#   Copyright 2010 VMware, Inc.  All rights reserved.
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# 

#
#   Support program for VMware Studio VAMI on Linux hosts.
#
#   Collects various configuration and log files
#   for use when troubleshooting VMware Studio VAMI on Linux hosts.
#


#   Function: usage prints how to use this script

export TEXTDOMAINDIR=/opt/vmware/lib/locale
export TEXTDOMAIN=vamisupport


function usage {
   echo ""
   echo `gettext "Usage:"` "$0 [-h] [-w" `gettext "output directory"` "] [-q] [-P]"
   echo "  -h" `gettext "prints this usage statement"`
   echo "  -q" `gettext "runs in quiet mode"`
   echo "  -w <dir>" `gettext "sets the working directory used for the output files"`
   echo "  -P" `gettext "don't include running processes state"`
   exit
}


TARFILE=vami-$(date -I).$$.tgz
VER=1.00
OUTPUT_DIR=vami-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 `gettext "\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

   if [ -z "$FILE" ]; then
       return
   fi

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

      if [ $? != 0 ]; then
         banner "`gettext 'Could not create'` ./${OUTPUT_DIR}$DIR... " \
                "`gettext 'Have you run out of disk space?'`" "`gettext '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
            if [ $collect_state_logs -eq 1 ]
            then
                cp -pr "$line" "${OUTPUT_DIR}$DIR" 2>/dev/null
            else
                if [ `expr "$line" : '.*state.*log'` -eq 0 ]
                then
                    cp -pr "$line" "${OUTPUT_DIR}$DIR" 2>/dev/null
                fi
            fi

            # 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 "`gettext 'Could not copy'` $line `gettext '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: addtar_smallfile calls the addtar
#   command on the given file(s) only if the file
#   size if less than 30K.

function addtar_smallfiles {
   FILES=$1
   for file in $FILES
   do
      if [ -e "$file" ]; then
         size=`stat --format="%s" "$file"`
         if [ $size -lt 30000 ]; then
            addtar $file
         fi
      fi # does file exist
   done # for each file in the list
}

#
# expungeSensitiveFiles
#
# List of files that we don't want to keep.
#
function expungeSensitiveFiles {
   # list of files that shouldn't be included
   EXPUNGELIST="/etc/vmware/ssl"

   for FILE in ${EXPUNGELIST}
   do
      rm -rf ${OUTPUT_DIR}$FILE 2>/dev/null
   done
}

#   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 "`gettext 'Either could not run'` $1 `gettext 'or could not write to'` $2" \
             "`gettext 'Do you have a full disk?'`" "`gettext 'Continuing...'`"
   else
      addtar "$2"
      rm "$2"

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

quiet_mode=0
working_dir=
collect_state_logs=0
proc_mode=1

# main()
while getopts hqw:P arg ; do
   case "$arg" in
      "q") quiet_mode=1 ;;
      "w") working_dir=$OPTARG ;;
      "P") proc_mode=0 ;;
      "h") usage ;;
        *) usage ;;
   esac
done

#   Start message

banner "`gettext 'Support program for VMware Studio VAMI on Linux hosts - Version'` $VER"

#   Check for root privledge

if [ $(id -u) != "0" ]; then
   banner "`gettext '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 "`gettext '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 "`gettext '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 "`gettext 'Could not create'` ./${OUTPUT_DIR}... `gettext 'Exiting...'`" \
          "`gettext 'Please cd to a directory to which you can write'`"
   exit
fi

# Add VAMI
addtar "/opt/vmware/var/log/*"
addtar "/opt/vmware/etc/appliance*"
addtar "/opt/vmware/etc/studio*"
addtar "/opt/vmware/etc/lighttpd/lighttpd.conf"
addtar "/opt/vmware/etc/sfcb/sfcb.cfg"
addtar "/opt/vmware/etc/vami/vami.xml"
addtar "/opt/vmware/var/lib/vami/services.xml"
addtar "/opt/vmware/var/lib/vami/update/data/info/*"
addtar "/opt/vmware/var/lib/vami/update/data/manifest/*"
addtar "/opt/vmware/var/lib/vami/update/data/signature.key"
addtar "/opt/vmware/var/lib/vami/update/data/validate-signature-script"
addtar "/opt/vmware/share/htdocs/VAMI*.xml"
addtar "/opt/vmware/etc/isv/*"

for service in $(ls /opt/vmware/var/lib/vami)
do
   if [ -d /opt/vmware/var/lib/vami/$service ]; then
      addtar "/opt/vmware/var/lib/vami/$service/service.xml"
      addtar "/opt/vmware/var/lib/vami/$service/provider/provider*.xml"
      addtar "/opt/vmware/var/lib/vami/$service/conf/lighttpd.conf"
      addtar "/opt/vmware/share/htdocs/service/$service/view*.xml"
   fi
done

# 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

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/security/*"

# Add services
addtar "/etc/services"

addtar "/var/log/boot*"
addtar "/var/log/secure*"
addtar "/var/log/messages*"
addtar "/var/log/vmware/*"

if [ $proc_mode -eq 1 ]; then
   # 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\|pagemap\|/proc/$$`
   do
      addtar "$procfile"
   done
fi

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

runcmd "echo vami-support version: $VER" "/tmp/vami-support-version.$$.txt"
runcmd "cat $(readlink -f $0)" "/tmp/vami-support-script.$$.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)
which rpm > /dev/null 2>&1
if [ $? -eq 0 ]; then
   runcmd "rpm -qa" "/tmp/rpm-qa.$$.txt"
else
   which dpkg > /dev/null 2>&1
   if [ $? -eq 0 ]; then
      runcmd "dpkg -l" "/tmp/dpkg-l.$$.txt"
   fi
fi
runcmd "netstat -lan" "/tmp/netstat-lan.$$.txt"
runcmd "route -n" "/tmp/route.$$.txt"
runcmd "mount" "/tmp/mount.$$.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

expungeSensitiveFiles

#   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 "`gettext 'The tar did not successfully complete!'`" \
          "`gettext 'If tar reports that a file changed while reading, please attempt to rerun this script.'`"
else
   banner "`gettext 'File:'` $(pwd | sed s/[\/]$//)/$TARFILE" \
          "`gettext 'Please attach this file when submitting an incident report.'`"
fi


#   Clean up temporary files

rm -rf $OUTPUT_DIR

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

#   End
