#!/bin/sh
#
# Script:	hacdiag
#
# Description:	Gather diag info from HAC products
#
# Platform:	Solaris/Linux/HP-UX/AIX/Darwin
#
# Authors:	(c) High-Availability.Com Limited
#
# $Id$
AUTO="false"
NEXENTA="false"

VERSION='$Id$'
DIAGV="`echo $Revision$|awk '{print $2}'`"
DOMAINNAME=`domainname`
UNAME=`uname -n`.${DOMAINNAME:-"UnsetDomain"}
NETCAT="/opt/HAC/bin/nc"

RSF_DIR=/opt/HAC/RSF-1

HACBASEVERSIONFILE="/opt/HAC/.version"
RSFVERSIONFILE="/opt/HAC/RSF-1/.version"
RSFGUIVERSIONFILE="/opt/HAC/RSF-1/.gui_version"
CHKCONFIG="/opt/HAC/bin/hac_check_config"
CUSTINFO="/opt/HAC/.custinfo"

#
# Global settings
DATE="`date '+%Y%m%d'`"
if [ -x /opt/HAC/bin/hac_hostid ]; then
	HOSTID="`/opt/HAC/bin/hac_hostid`"
else
	HOSTID="`hostid`"
fi
TARFILE="/var/tmp/HACdiag-`uname -n`-${HOSTID}-${DATE}.tar"	# final output
INFOFILE="/var/tmp/HACdiag-`uname -n`-${HOSTID}-${DATE}.info"	# general system info
COMPANY_NAME="High-Availability.Com"
NOTDONE="/tmp/$$.hacdiag-non-files"
DIRECTORYLIST="/opt/HAC /etc/init.d /sbin/init.d /etc/rc.config.d /etc/services /var/adm/message* /var/log/message* /var/log/system.log* /var/adm/syslog"
SUMMARY_STATUS=0

export TARFILE INFOFILE COMPANY_NAME DATE HOSTID NOTDONE DIRECTORYLIST SUMMARY_STATUS CUSTINFO CHKCONFIG

#
# Parse command line args
while [ $# -gt 0 ]; do
    case "$1" in
	    -a) AUTO="true"
	    ;;

	    -n) NEXENTA="true"
	    ;;
	esac
    shift
done

#
# Function:    get_logsize
# Description: chech the log directory is no larger than 10M
# Returns:
# Args:
#      $* : none
get_logsize()
{
    Ksize=`du -sk /opt/HAC/RSF-1/log/| awk '{print $1}'`
    Msize=`expr ${Ksize} / 1024`
    if [ ${Msize} -gt 10 ] ; then
	echo "#" >> ${INFOFILE}
	echo "# WARNING: RSF-1 log directory is greater than 10 Meg (${Msize} Mbytes)!!!" >> ${INFOFILE}
	echo "#" >> ${INFOFILE}
    fi
}

#
# Function:    echo_nn
# Description: echo args with no newline at the end.
# Returns:     nowt
# Args:
#      $* : why, it's the stuff we want echoing
echo_nn()
{
    case `echo '\c'` in     # Check for BSD or SYSV style echo
        '\c')   echo -n "${1}"          ;;
        *)      echo "${1}\c"           ;;
    esac
}

#
# Function:    search_program
# Description: Check to see if a program exists in a path
# Returns:     0 if all OK, otherwise != 0
# Args:
#      $1 : the program to find
#      $2 : the path to search
search_program()
{
    if [ $# -lt 2 ] ; then
	return 1
    fi

    _PATH_=`echo $2|sed 's/:/ /g'`
    for search_path in ${_PATH_} ; do
	if [ -f "${search_path}/$1" -a -x  "${search_path}/$1" ] ; then
	    echo "${search_path}/$1"
	    return 0
	fi
    done
    return 2
}

#
# Function:    zip_file
# Description: Compress/Gzip the file given as first argument.
# Returns:     0 if all OK, otherwise != 0
# Args:
#      $1 : the file to compress
zip_file()
{
    if [ $# -ne 1 ] ; then
	return 1
    fi

    zipper=`search_program compress "/bin /sbin /usr/bin /usr/sbin"`
    if [ -n "${zipper}" ] ; then
	${zipper} "$1"
	if [ $? -eq 0 ] ; then
	    echo "$1.Z"
	    return 0
	fi
    fi

    zipper=`search_program gzip "/bin /sbin /usr/bin /usr/sbin"`
    if [ -n "${zipper}" ] ; then
	${zipper} "$1"
	if [ $? -eq 0 ] ; then
	    echo "$1.gz"
	    return 0
	fi
    fi

    return 2
}


#
# Function:    sep
# Description: Output command details to the gathered diag file.
# Args:
#      -i : the comment is for an included file
#      -c : add the second arg as a comment line
#      $@ : the command and args being run
sep()
{
    break_line
    echo "#" >> ${INFOFILE}

    if [ x"${1}" = x"-i" ] ; then
	shift
	echo "# The file \"$*\" contains (remember indented by one space):" >> ${INFOFILE}
    else
	#
	# A comment entry
	if [ "${1}" = "-c" ] ; then
	    shift
	    echo "$@" >> ${INFOFILE}
	else
	    echo "# Running the command \"$*\" produced: " >> ${INFOFILE}
	fi
    fi
    echo "#" >> ${INFOFILE}
}

#
# Function:    break_line
# Description: Output a fixed size seperator to the file of gathered diags
# Args:
#      NONE
break_line()
{
    echo '###############################################################################################' >> ${INFOFILE}
}

#
# Function:    get_custinfo
# Description: extract and return data from the customer information file
# Args:
#      Field to extract and return
get_custinfo()
{
    if [ -f ${CUSTINFO} ] ; then
	VAL=`cat ${CUSTINFO}|grep "$1" | awk -F= '{print $2}'|sed -e 's/\"//g' -e 's/ /_/g'`

	if [ -z "${VAL}" ] ; then
	    echo "Unset"
	else
	    echo "${VAL}"
	fi
    else
	echo "Unset"
    fi
}

#
# Function:    hacdiag_heading()
# Description: Produce a heading for the diagnostics file, providing enough
#              trace information as possible.
# Args:
#      NONE
hacdiag_heading()
{
    rm -f ${INFOFILE}
    rm -f ${NOTDONE}
    break_line
    echo '#                                                                                             #' >> ${INFOFILE}
    echo '#               _|    _|    _|_|      _|_|_|  _|_|_|    _|                                    #' >> ${INFOFILE}
    echo '#               _|    _|  _|    _|  _|        _|    _|        _|_|_|    _|_|_|                #' >> ${INFOFILE}
    echo '#               _|_|_|_|  _|_|_|_|  _|        _|    _|  _|  _|    _|  _|    _|                #' >> ${INFOFILE}
    echo '#               _|    _|  _|    _|  _|        _|    _|  _|  _|    _|  _|    _|                #' >> ${INFOFILE}
    echo '#               _|    _|  _|    _|    _|_|_|  _|_|_|    _|    _|_|_|    _|_|_|                #' >> ${INFOFILE}
    echo '#                                                                           _|                #' >> ${INFOFILE}
    echo '#                                                                         _|_|                #' >> ${INFOFILE}
    echo '#                                                                                             #' >> ${INFOFILE}

    break_line

    if [ -f ${RSF_DIR}/bin/rsfcli ] ; then
	${RSF_DIR}/bin/rsfcli stat >> ${INFOFILE} 2>&1
	SUMMARY_STATUS=$?
    fi

    if [ -f ${CUSTINFO} ] ; then
	#
	# CUSTOMER_NAME
	# CONTACT
	# EMAIL
	# PHONE
	CUSTOMER_NAME=`get_custinfo CUSTOMER_NAME`
	CONTACT=`get_custinfo CONTACT`
	EMAIL=`get_custinfo EMAIL`
	PHONE=`get_custinfo PHONE`
	echo "# Customer name: ${CUSTOMER_NAME}" >> ${INFOFILE}
	echo "# Contact name:  ${CONTACT}" >> ${INFOFILE}
	echo "# EMail:         ${EMAIL}" >> ${INFOFILE}
	echo "# Phone:         ${PHONE}" >> ${INFOFILE}
    else
	echo "#"
	echo "# No Customer information file exists: ${CUSTINFO}" >> ${INFOFILE}
	echo "#"
    fi

    get_logsize

    if [ -x "${CHKCONFIG}" ] ; then
	break_line
	echo "Running ${CHKCONFIG} produced:" >> ${INFOFILE}
	${CHKCONFIG} >> ${INFOFILE}
    fi

    break_line
    echo '#' >> ${INFOFILE}
    echo "#  Version: ${VERSION}" >> ${INFOFILE}
    echo "# Run time: `date`"     >> ${INFOFILE}
    echo "#  Host ID: ${HOSTID}"  >> ${INFOFILE}
    echo "#   System: `uname -a`" >> ${INFOFILE}
    if [ -f ${HACBASEVERSIONFILE} ] ; then
	echo "# HACBASE V: `cat ${HACBASEVERSIONFILE}`" >> ${INFOFILE}
    fi
    if [ -f ${RSFVERSIONFILE} ] ; then
	echo "#  RSF-1 V: `cat ${RSFVERSIONFILE}`" >> ${INFOFILE}
    fi
    if [ -f ${RSFGUIVERSIONFILE} ] ; then
	echo "# RSFGUI V: `cat ${RSFGUIVERSIONFILE}`" >> ${INFOFILE}
    fi
    echo '#' >> ${INFOFILE}

    echo "Saving diagnostic information to ${INFOFILE}."
    echo_nn "Generating and collecting diagnostic information, please wait..."
}

#
# Function:    put_diag()
# Description: Send gathered diagnostic information to HAC
#
# Args:
#      $1 : file to send
put_diag()
{
    FILE=${1}
    WEBDIAG=/tmp/complete$$
    DIAGFILE=/tmp/data$$
    #
    # Remember the boundary in the message is the defined boundary with a preceeding '--'
    BOUNDARY="HACDiagOutput-MultiPart-Generating-PID-is-$$"
    BODYBOUNDARY="--${BOUNDARY}"

    if [ -x "${NETCAT}" ] ; then
	if [ "${AUTO}" = "false" ] ; then
	    echo_nn "Upload to ${COMPANY_NAME} (requires an Internet connection) (y/n): "
	    read ans
	    case "${ans}" in
		n*|N*) DOIT=N ; break;;
		*) DOIT=Y ; break;;
	    esac
	else
	    DOIT=Y
	fi

	if [ x"${DOIT}" = x"N" ]; then
	    echo "Please send file \"${1}\" to ${COMPANY_NAME} (EMail: support@${COMPANY_NAME})."
	else
	    echo ${BODYBOUNDARY} > ${DIAGFILE}
	    echo 'Content-Disposition: form-data; name="thefile"; filename="'${FILE}'"' >> ${DIAGFILE}
	    echo "Content-Type: application/octet-stream" >> ${DIAGFILE}
	    echo "" >> ${DIAGFILE}
	    cat "${FILE}" >> ${DIAGFILE}
	    #
	    # Add an extra newlins as tar files generally don't
	    echo "" >> ${DIAGFILE}

	    #
	    # Insert the diag file as text output.
	    echo ${BODYBOUNDARY} >> ${DIAGFILE}
	    echo 'Content-Disposition: form-data; name="inlinediag"; filename="notneeded"' >> ${DIAGFILE}
	    echo "Content-Type: text/plain; charset=us-ascii" >> ${DIAGFILE}
	    echo "" >> ${DIAGFILE}

	    #
	    # Get rid of the first 10 lines (hacdiag in big letters) as it looks bad in html...
	    cnt=0;
	    while read line
	      do
	      if [ ${cnt} -lt 10 ] ; then
		  cnt=`expr $cnt + 1`
	      else
		  echo "${line} <br>" >> ${DIAGFILE}
	      fi
	    done < ${INFOFILE}

	    #
	    # Send as form.
	    echo ${BODYBOUNDARY} >> ${DIAGFILE}
	    echo 'Content-Disposition: form-data; name="Submit"' >> ${DIAGFILE}
	    echo "" >> ${DIAGFILE}
	    echo "Submit Query" >> ${DIAGFILE}
	    echo ${BODYBOUNDARY} >> ${DIAGFILE}

	    DSIZE=`wc -c ${DIAGFILE} | awk '{print $1}'`
	    echo "OK, sending ${DSIZE} bytes to ${COMPANY_NAME}: diag saved in ${WEBDIAG}"

	    echo "POST /diag/post_diag.php?hostid=${HOSTID}&DIAGV=${DIAGV}&UNAME=${UNAME}&CUSTOMER=${CUSTOMER_NAME}&CONTACT=${CONTACT}&SUMMARYSTATUS=${SUMMARY_STATUS} HTTP/1.1" > ${WEBDIAG}
	    echo "Content-Type: multipart/form-data; boundary=${BOUNDARY}" >> ${WEBDIAG}
	    echo "Host: diag.${COMPANY_NAME}" >> ${WEBDIAG}
	    echo "Content-Length: ${DSIZE} " >> ${WEBDIAG}
	    echo "" >> ${WEBDIAG}

	    cat ${DIAGFILE} >> ${WEBDIAG}

	    #cat ${WEBDIAG} | eval ${NETCAT} -v -v -v -v -w 5 diag.${COMPANY_NAME} 80
	    res=`cat ${WEBDIAG} | eval ${NETCAT} -w 5 diag.${COMPANY_NAME} 80 2>/dev/null`

	    if [ $? -eq 0 ] ; then
		echo "Complete; tidying up temporary files."
		rm -f ${1} 2>/dev/null
	    else
		echo ""
		echo "HTTP upload of diagnostics failed (ports restricted?)"
		echo "Please manually send the file \"${1}\" to ${COMPANY_NAME}:"
		echo "(ANON FTP: ftp.${COMPANY_NAME})";
		echo "(EMail: support@${COMPANY_NAME})."
	    fi
	    rm -f ${WEBDIAG} ${DIAGFILE}
	fi
    else
	echo "Please send file \"${1}\" to ${COMPANY_NAME} (EMail: support@${COMPANY_NAME})."
    fi
}

#
#
interrogate_crontab()
{
    CRONOUT="/tmp/cronout.$$"
    crontab -l 2>/dev/null | grep -v '^#' | grep -v '^$' | awk '{print $6}' > ${CRONOUT}

    while read FILE
      do
      file ${FILE} | grep -i 'shell script text' > /dev/null 2>&1

      if [ $? = 0 ] ; then
	  echo "#######################################################################" >> ${INFOFILE}
	  echo "# Contents of crontab run shell script ${FILE}" >> ${INFOFILE}
	  echo "#######################################################################" >> ${INFOFILE}
	  while read line
	    do
	    echo "+ $line">> ${INFOFILE}
	  done < ${FILE}
      else
	  echo "#######################################################################" >> ${INFOFILE}
	  echo "# crontab entry ${FILE} is NOT a shell script" >> ${INFOFILE}
	  echo "#######################################################################" >> ${INFOFILE}
      fi
    done < ${CRONOUT}

    rm ${CRONOUT}
}

#
# Ask RSF-1 for a state dump.
run_rsfdump()
{
    if [ -f ${RSF_DIR}/bin/rsfdump ] ; then
	sep ${RSF_DIR}/bin/rsfdump
	${RSF_DIR}/bin/rsfdump
	if [ -f /tmp/rsfmon.stat ] ; then
	    cat /tmp/rsfmon.stat >> ${INFOFILE} 2>/dev/null
	fi
    else
	if [ -f ${RSF_DIR}/bin/rsfcli ] ; then
	    sep ${RSF_DIR}/bin/rsfcli dump
	    ${RSF_DIR}/bin/rsfcli dump >> ${INFOFILE} 2>&1
	    if [ -f /tmp/rsfmon.stat ] ; then
		cat /tmp/rsfmon.stat >> ${INFOFILE} 2>/dev/null
	    else
		echo "RSF-1 needs to be updated for rsfcli dump to work" >> ${INFOFILE} 2>/dev/null
	    fi
	fi
    fi
    echo >> ${INFOFILE}
}

#
# Ask RSF-1 for a status
run_rsfstat()
{
    if [ -f ${RSF_DIR}/bin/rsfstat ] ; then
	sep ${RSF_DIR}/bin/rsfstat
	${RSF_DIR}/bin/rsfstat >> ${INFOFILE} 2>&1
    else
	if [ -f ${RSF_DIR}/bin/rsfcli ] ; then
	    sep ${RSF_DIR}/bin/rsfcli status
	    ${RSF_DIR}/bin/rsfcli status >> ${INFOFILE} 2>&1
	fi
    fi
    echo >> ${INFOFILE}
}

#
#
run_ps()
{
    if [ -f /usr/ucb/ps ]; then
	sep "/usr/ucb/ps auxww"
	/usr/ucb/ps auxww >> ${INFOFILE} 2>&1
    else
	sep "ps -elf"
	ps -elf >> ${INFOFILE} 2>&1
    fi
    echo >> ${INFOFILE}
}

#
#
show_lastlogin()
{
    sep "last"
    last | head -20 >> ${INFOFILE}
    echo >> ${INFOFILE}
}

#
#
show_etc()
{
    sep "Files in /etc with mtime of 300"
    find /etc -mtime -300 -print >> ${INFOFILE} 2> /dev/null
    echo >> ${INFOFILE}
}

#
#
show_sbin()
{
    sep "Files in /sbin"
    find /sbin -print >> ${INFOFILE} 2> /dev/null
    echo >> ${INFOFILE}
}

insert_sar()
{
    if [ -x /usr/sbin/sar ]; then
	if [ -f /var/adm/sa/sa`date +%d` ]; then
	    sep sar -A
	    sar -A >> ${INFOFILE} 2> /dev/null
	    echo >> ${INFOFILE}
	fi
    fi
}

#
# Function:    run_program()
# Description:
#
# Args:
#      $1 : full command line
run_program()
{
    PROG=$1
    FULL=$*

    case "${PROG}" in
	/*)
           if [ -x ${PROG} ] ; then
	       sep ${FULL}
	       ${FULL} >> ${INFOFILE} 2>&1
	       echo >> ${INFOFILE}
           else
	       if [ -f ${PROG} ] ; then
		   echo "The program \"${FULL}\" was not run as it is not executable" >> ${NOTDONE}
	       else
		   echo "The program \"${FULL}\" was not run as it did not exist" >> ${NOTDONE}
	       fi
           fi
           ;;
	*)

	   result=`search_program ${PROG} "${PATH}"`
	   if [ $? -eq 0 ] ; then
	       sep ${FULL}
	       ${FULL} >> ${INFOFILE} 2>&1
	       EXITCC=$?
	       echo >> ${INFOFILE}
	       echo "This command completed with exit code ${EXITCC}" >> ${INFOFILE}
	       echo >> ${INFOFILE}
	   else
	       echo "The program \"${FULL}\" was not run as it was not found in the search path: \"${PATH}\"" >> ${NOTDONE}
	   fi
	;;
    esac

}

#
# This routine attached the contents of the file given in the first arg.
insert_file()
{
    FILES=$@

    for FILE in ${FILES} ; do
	if [ -f ${FILE} ] ; then
	    sep -i ${FILE}
	    while read line
	      do
	      echo " ${line}" >> ${INFOFILE}
	    done < ${FILE}
	else
	    echo "The file \"${FILE}\" was not included as it did not exist" >> ${NOTDONE}
	fi
	echo >> ${INFOFILE}
    done
}

#
#
include_notdone()
{
    sep -c "# Output from the following files, programs etc was not included:"
    cat ${NOTDONE} >> ${INFOFILE}
    rm -f ${NOTDONE}
}

#
#
generate_tapearchive()
{
    echo_nn "Generating tape archive."
    BACK=`pwd`
    EXCLUDE="/tmp/diag-exclude"

    echo "./opt/HAC/java"            > ${EXCLUDE}
    echo "./opt/HAC/RSF-1/java"     >> ${EXCLUDE}
    echo "./opt/HAC/RSF-1/etc/java" >> ${EXCLUDE}

    sep -c "# The tar archive contains the following directories/files:"
    for directory in ${DIRECTORYLIST} ; do
	echo "# ${directory}" >> ${INFOFILE}
    done
    echo "#" >> ${INFOFILE}

    #
    # Clear tar file and start it with the customer information file itself.
    echo_nn "."
    rm -f ${TARFILE}
    cd `dirname ${INFOFILE}`
    tar cfh ${TARFILE} `basename ${INFOFILE}` 2> /dev/null

    for directory in ${DIRECTORYLIST} ; do
      echo_nn "."
      cd /
      #
      # tar r[eplace] h[follow sym links] f[ile] X[exclude files]
      tar rhfX ${TARFILE} ${EXCLUDE} .${directory} 2> /dev/null
    done

    echo "complete"
    rm -f ${EXCLUDE}
    cd ${BACK}
}

#
#
insert_metastat()
{
    if [ -x /usr/sbin/metastat ] ; then
	sep metastat
	/usr/sbin/metastat >> ${INFOFILE} 2>&1
	#
	# If there are diskset files then run metastat on each one.
	if [ -f "`ls /opt/HAC/RSF-1/etc/fs/disksets.* 2>/dev/null | head -1`" ] ; then
	    list=`grep -v '^#' /opt/HAC/RSF-1/etc/fs/disksets.*`
	    sep -c "# Running metastat on RSF-1 disksets \"${list}\":"
	    for set in ${list} ; do
		sep /usr/sbin/metastat -s ${set}
		/usr/sbin/metastat -s ${set} >> ${INFOFILE}
	    done
	else
	    echo "#" >> ${INFOFILE}
	    echo "# Note! there are no RSF-1 diskset control files in /opt/HAC/RSF-1/etc/fs/" >> ${INFOFILE}
	    echo "#" >> ${INFOFILE}
	    break_line
	fi
    else
	 echo "SDS is not installed on this machine" >> ${NOTDONE}
    fi
}

if [ "${NEXENTA}" = "true" ] ; then
    echo "No NexentaStor diagnostics at this time."
else
    hacdiag_heading
    run_rsfdump
    run_rsfstat
    run_ps
    run_program ${NETCAT} --version
    run_program mount
    run_program arch
    run_program /usr/bin/arch -k
    run_program df -k
    run_program swap -l
    run_program crontab -l
    interrogate_crontab
    run_program uptime
    run_program ifconfig -a
    run_program /opt/HAC/bin/ifstat
    run_program netstat -rvni
    run_program netstat -an
    run_program arp -a
    run_program modinfo
    run_program pkginfo
    run_program showrev -p
    run_program showrev
    run_program isainfo -kv
    run_program isainfo -bv
    run_program isainfo -v
    run_program /usr/platform/`uname -m`/sbin/prtdiag -v
    run_program /opt/ignite/bin/print_manifest -p
    run_program /usr/sbin/prtconf -p
    run_program /usr/sbin/psrinfo
    run_program /usr/bin/ipcs
    run_program /usr/sbin/vxprint
    run_program /usr/sbin/vxdg list
    run_program /usr/sbin/vxtask list
    show_lastlogin
    show_etc
    show_sbin
    insert_metastat
    insert_sar
    insert_file /etc/hosts
    insert_file /etc/hostname*
    insert_file /etc/vfstab
    insert_file /etc/fstab
    insert_file /etc/nsswitch.conf
    insert_file /etc/networks
    insert_file /etc/netmasks
    insert_file /etc/nodename
    insert_file /etc/system
    insert_file /etc/resolv.conf
    insert_file /etc/path_to_inst
    insert_file /etc/default/login
    insert_file /usr/local/conf/ht*conf
    insert_file /etc/apache/ht*conf
    insert_file /usr/local/apache/conf/ht*conf
    include_notdone
    echo ".done"
    generate_tapearchive
fi

rm -f ${TARFILE}.gz ${TARFILE}.Z
zipped_file=`zip_file ${TARFILE}`
EXIT=$?


if [ ${EXIT} -eq 0 ]
then
	echo "Output file is ${zipped_file}"
	put_diag ${zipped_file}
else
	echo "Failed to compress, output file is ${TARFILE}"
	put_diag ${TARFILE}
fi
