#!/bin/bash
#
# HWCFG - Linux hardware configuration report utility
#	  This is a proc based HW configuration reporting utility.
#	  Because of this, hwcfg cannot report on items not configured 
#	  or listed in some way in the proc filesystem (/proc).
#
#	 To date this has only been tested on RedHat systems, but it should
#	 work for other distributions as well.
#
# Note: There are some things that hwcfg is NOT: 
#       - It is not meant to give details on things that are not completely HW.
#	   e.g. disk partitions this a logical property of the disk, etc.
#	- It does not do any active scans to report what is in the system (no 
#	  PCI bus scans, reading BIOS, or EISA CMOS settings).
#	
# Debugging: 
#	1) Please verify that you have the latest version first (RCS info below)
#	2) If you still have problems then do the following:
#	   Please mail me (in tar or cpio format):
#		- the output of hwcfg (and standard error)
#		- a description of what was incorrect or missing
#		- the cpio file created by debug_hwcfg.sh. Download 
#		  debug_hwcfg.sh and run it. This will gather the output of 
#		  certain commands and the contents of certain files that form 
#		  the input to hwcfg. This shell will produce a cpio file of the
#		  data I need.
#
#	I will try to duplicate your problem and fix it as soon as possible.
#
#	Or if you want to, fix it yourself and mail me this fixes!
#
#       - Lars Nordin
#
#  Web Page:  home.integrityonline.com/lnordin/hwcfg.html
#  Contact:   lnordin@iol10.com
#
# $Id: hwcfg,v 0.63 1998/12/21 06:53:16 lars Exp lars $
# {NOTE: add more RCS info}
#

# TO DOs
# 1 - Make parsing less positional dependent and more text/string intelligent
# 2 - Add documentation; including a man page
# 3 - Process more architectures
# 4 - Report extended information from RAID controllers 
# 5 - Report memory mapping of devices (if possible)
# 6 - Port to PERL (I need to learn it one of these days)

# ***  Functions  ***
# Function to print our usage message
Usage(){
 echo >&2 "Usage: hwcfg [-d]
Function: Display the HW configuration of the system; I/O, IRQ, DMA, etc.

Options:         (Description)                 (Default)
  -d             Get more information from 	Off; all information is gathered
		 dmesg kernel log buffer.	from the /proc fs and commands.
		 NOTE: This option is only 
		 useful if the system was 
		 recently booted."
 exit 1
}

#
# Function to clean up temp directory
CleanUp(){
 #
 # Remove tmp files 
 if [ -d ${TEMPPATH} ]
 then
	rm -rf ${TEMPPATH} 
 fi
}

# **** BEGIN MAIN ****
#
# Data and globals, etc.
# TRUE=1 FALSE=0
DMESG=0
# The following flag gives a method for testing the script on my system where 
#  the /proc and other input is pulled from other files so that I can test
#  and fix the script using input from other peoples systems
UNDERTEST=0
TEMPPATH=/tmp/temphwcfgdir  # It should be very unlikely that this dir exists

# Parse arguments
while getopts dt? c; do
  case $c in
   d) # Use information gathered from dmesg command to suppliment info. 
      DMESG=1
      ;;
   t) # Use alternate data sources for testing 
      UNDERTEST=1
      ;;
   '?') # any other switch     *** May need wildcard (*) to catch rest 
      Usage 
      ;;
  esac
done

# setting to ease testing with test data and cross-platform debugging
# set data sources

# *** Implement alternate source for hdparm, setserial ***

if [ $UNDERTEST -eq 1 ]
then
     echo " ** IN TESTING MODE **"
     PROCPATH=./proc
     FREE="cat ./free"
     UNAMEM="cat ./uname"
     DMESGC="cat ./dmesg"
     IFCONFIGC="cat ./ifconfig"
else
     # normal mode from the real sources
     PROCPATH=/proc
     FREE=free
     UNAMEM="uname -m"
     DMESGC=dmesg
     IFCONFIGC="ifconfig -a"
fi

# set for new temp files
CleanUp
mkdir ${TEMPPATH}

#
# Main() 
#
CPU="`$UNAMEM`"
#
# Sort by CPU archetechture
case $CPU in 
	i386) ARCH="x86";;
	i486) ARCH="x86";;
	i586) ARCH="x86";; # Pentium, AMD K6, presume Cyrix CPUs as well
     # Pentium Pro? i586 or i686?
	i686) ARCH="x86";;  # Pentium 2
 	alpha) ARCH="alpha";;
     # For Sparc, PowerPC, MIPS, 68k, Amiga, etc.
	*) ARCH="other";;
esac

#
# Process CPU info
#
case "$ARCH"
in
    # for intel 
    "x86" )  grep -E "processor|cpu	|model|vendor_id" ${PROCPATH}/cpuinfo | sed 's/	//g' | awk 'BEGIN{FS="[:\n]";RS=""}{printf(" %-8s  %-6s %-12s %-15s \n",toupper($1),toupper($3),toupper($5),toupper($7));printf(" %8s  %-6s %-12s %-15s\n" , $2, $4, $6, $8) }';; 

    # for alpha grep the 3 lines I wanted then convert field 1 into row 1 & 
    #     convert to uppercase (title row) and and field 2 into row 2 (data)
    alpha)  grep -E "cpu	|system type|string" ${PROCPATH}/cpuinfo | sed 's/	//g' | awk 'BEGIN{FS="[:\n]";RS=""}{printf(" %-6s  %-10s  %-24s\n",toupper($1),toupper($3),toupper($5));printf(" %-6s  %-10s  %-24s\n" , $2, $4, $6) }';; 
   # All other CPUs
   * ) echo " Unable to obtain cpu information for your architecture." >&2
       echo "  Please mail the file /proc/cpuinfo to lnordin@iol10.com" >&2
       echo "  for inclusion of your CPU architecture." >&2 ;;
esac
echo



# Report memory
#
echo -e "Memory: \c"
$FREE | grep "Mem:" | awk '{printf(" %s kB\n", $2)}'
if [ ${DMESG} -eq 1 ]
then
  # get memory from dmesg
  echo -e "-- Memory info from dmesg command --"
  MOREMEM=`$DMESGC | grep Memory | cut -d" " -f2 | cut -d"/" -f2`
  if [ -z "$MOREMEM" ]
  then
      echo $MOREMEM
  fi
  echo
fi




# Create I/O, IRQ, DMA map a la SCO UNIX hwconfig
#

# Copy files with info to scratch files
cat ${PROCPATH}/interrupts > ${TEMPPATH}/irqs
cat ${PROCPATH}/ioports > ${TEMPPATH}/ios
cat ${PROCPATH}/dma > ${TEMPPATH}/amd
#
# Clean up data files for sorting
# Remove "+" and spaces at the beginning of lines, and format so that
# ":" is the field delimeter.
for hwfile in ${TEMPPATH}/irqs ${TEMPPATH}/ios ${TEMPPATH}/amd
do 
ed $hwfile > /dev/null 2>&1 <<!
g/ : /s//:/g
g/: /s//:/g
g/^ /s///g
g/+/s/+//g
w
q
!
done
#
# Clean up irqs file
# NOTE: /proc/interrupt is different for different architectures!
#
# if i386 then Remove CPU field and NMI field
if [ "$ARCH" = "x86" ]
then
  grep -v NMI ${TEMPPATH}/irqs | grep -v CPU > ${TEMPPATH}/larz
  mv ${TEMPPATH}/larz ${TEMPPATH}/irqs
fi
# Swap fields
if [ "$ARCH" = "x86" ]
then
 awk 'BEGIN { ORS = " "} { printf ("%s", $1); for (x = 4; x <= NF; ++x) { print $x} printf ("\n") }' ${TEMPPATH}/irqs > ${TEMPPATH}/larz
else
 awk 'BEGIN { ORS = " "} { printf ("%s", $1); for (x = 3; x <= NF; ++x) { print $x} printf ("\n") }' ${TEMPPATH}/irqs > ${TEMPPATH}/larz
fi
mv ${TEMPPATH}/larz ${TEMPPATH}/irqs
#
# Create key/index file
# Sort IRQs
# Copy certain IRQ values into key file
awk 'BEGIN { FS = ":"; ORS = " "} { if ($1 > 2 && $1 != 6 && $1 != 8 && $1 != 13) { printf ("%s\n", $2)} }' ${TEMPPATH}/irqs > ${TEMPPATH}/hwkey
#
# Sort IO ports
# Copy certain IO port values into key file
awk 'BEGIN { FS=":"; ORS = " "} $1 !~ /^00/ && $2 != "floppy DIR" { for (x=2; x <= NF; ++x) {print $x}; printf ("\n") }' ${TEMPPATH}/ios >> ${TEMPPATH}/hwkey
##
# Sort DMA channels
# Copy certain DMA channel values into key file
awk 'BEGIN { FS=":"; ORS = " "} { if ($1 != 4) { for (x=2; x <= NF; ++x) {print $x}; printf ("\n")} }' ${TEMPPATH}/amd >> ${TEMPPATH}/hwkey
#
sort ${TEMPPATH}/hwkey | uniq > ${TEMPPATH}/sortkey
#
# Handle case of serial and serial(auto) keys
grep serial ${TEMPPATH}/sortkey >/dev/null 2>&1
if [ $? -eq 0 ]
then
    grep "serial(auto)" ${TEMPPATH}/sortkey >/dev/null 2>&1
    if [ $? -eq 0 ]
    then
	:
        ##grep -v "serial" ${TEMPPATH}/sortkey > ${TEMPPATH}/tmpkey
        ##cp ${TEMPPATH}/tmpkey ${TEMPPATH}/sortkey
        ##rm ${TEMPPATH}/tmpkey
    fi 
fi
#
#echo " Information from ${PROCPATH} files:"
echo
while read KEY 
do 
  IRQS=`grep "$KEY" ${PROCPATH}/interrupts | cut -d":" -f1`
  IOS=`grep "$KEY" ${PROCPATH}/ioports | cut -d":" -f1 | awk 'BEGIN{FS="\n";RS="";ORS=""}{for (x=1; x < NF; ++x) {printf("%s& ",$x)}; printf ("%s\n",$NF) }'`
  DMAS=`grep "$KEY" ${PROCPATH}/dma | cut -d":" -f1`
  echo "$KEY:$IRQS:$IOS:$DMAS"
done  < ${TEMPPATH}/sortkey | awk 'BEGIN{FS=":"; printf(\
"------------Unit  Irq\t    I/O Ports\t      DMA Channel------------\n")}{printf("%16s  %2s  %22s  %2s\n",$1,$2,$3,$4)}'



#
# Parse PCI info
#
PCIFILE=`cat ${PROCPATH}/pci | wc -l`
# Check that pci file has info
if [ ${PCIFILE} -gt 1 ] 
then
  echo
  echo "----PCI info--------"
# 
  grep -v "devices found:" ${PROCPATH}/pci |    \
  awk '{if ($0 ~ /:$/){    \
           if (NR != 1) {printf("|")}}    \
           else {print} }' | tr '\n' ' ' | sed 's/  / /g' | awk 'BEGIN{FS="[\.]";RS="|"}{printf("%s\n", $1); for (x=2; x<NF; x++ ) {if ($x ~ /I\/O/ || $x ~ /IRQ/ || $x ~ /memory at/ ) printf("%s",$x)}; printf("\n\n")}'
fi



#
# Process IDE info
#
# Create list of actual IDE Hard drives
IDELIST=""
for IDEDEV in hda hdb hdc hdd
do
	hdparm /dev/${IDEDEV} > /dev/null 2>&1
	if [ $? -eq 0 ]
	then
	  #echo "assign value to IDELIST"
	  IDELIST="${IDELIST} ${IDEDEV}"
	fi
done
#echo "IDELIST = $IDELIST"
if [ -n "${IDELIST}" ]
then
	echo
	echo "----IDE Devices--------"
	for IDEDEV in ${IDELIST}
	do
	  echo "Dev: ${IDEDEV}"
	  hdparm -i /dev/${IDEDEV} | grep -v "^$" | sed 's/: /=/' | awk 'BEGIN{FS="[,\n]";RS=""} \
{for (x=1; x<=NF; ++x) {      if ($x ~ /Model/)    {printf(" %s",$x)} \
		 	else {if ($x ~ /BuffSize/) {printf(" %s",$x)} \
			else {if ($x ~ /LBA/)      {printf(" %s",$x)} \
			else {if ($x ~ /PIO modes/)    {printf(" %s",$x)} \
			else {if ($x ~ /maxPIO/)    {printf(" %s",$x)} \
			else {if ($x ~ /DMA/)    {printf(" %s",$x)} \
			else {if ($x ~ /maxDMA/)    {printf(" %s",$x)} \
			else {if ($x ~ /CHS/)   {printf(" %s",$x)} }}}}}}} }; printf("\n") }'
	done
	if [ ${DMESG} -eq 1 ]
	then
	   # Parse dmesg info for ide
	   $DMESGC | grep ide > ${TEMPPATH}/idestuff 2>/dev/null
	   if [ $? -eq 0 ]
	   then
		echo "-- IDE info from dmesg command --"
		cat ${TEMPPATH}/idestuff
		for IDEDEV in hda hdb hdc hdd
		do
			$DMESGC | grep ${IDEDEV}
		done
	   fi #DMESG has data
  	fi #DMESG
fi #IDE

#exit




#
# Report SCSI
#
# check if SCSI dir exist
if [ -d ${PROCPATH}/scsi ]
then
   echo
   echo "----SCSI Devices--------"
   find ${PROCPATH}/scsi/* -type d -print | awk 'BEGIN{FS="/"}{print $4}'
   grep -v "Attached devices:" ${PROCPATH}/scsi/scsi | sed 'N
							s/\n/ /g
							N
							s/\n/ /g' | gawk 'BEGIN{RS="Host:"} $0 !~ /^$/{ printf(" %s Chan %s; Id %s; Lun %s:  %s %s \n", $1, $3, $5, $7, $9, $11); Chn=$3; SId=$5; Lun=$7;  }' 

   # for more info
   if [ ${DMESG} -eq 1 ]
   then
     # Parse dmesg info for scsi
     $DMESGC | grep scsi > ${TEMPPATH}/scsinfo
     if [ -s ${TEMPPATH}/scsinfo ]
     then
	echo "-- SCSI info from dmesg command --"
  	cat ${TEMPPATH}/scsinfo
     fi #Not null scsinfo in dmesg
   fi  #DMESG
fi #${PROCPATH}/scsi exists




#
# Parse ifconfig network info
#
echo
echo "----Network Devices--------"
# create file with which devices we want to view
grep -v Inter ${PROCPATH}/net/dev | grep -v face | grep -v lo | grep -v dummy \
  | grep -v ppp | cut -d":" -f1 > ${TEMPPATH}/netkeys
#
$IFCONFIGC > ${TEMPPATH}/ifconfig
for NETKEY in `cat ${TEMPPATH}/netkeys`
do
  #echo " key ==> ${NETKEY}"
  awk 'BEGIN{FS="\n";RS=""} $0 ~ /'"$NETKEY"'/{for (x=1; x <= NF; ++x) {if (x==1 || $x ~ /nterrupt/ || $x ~ /ase address/ ) printf("%s\n",$x); else continue }}' ${TEMPPATH}/ifconfig 
done


#
# Serial information
#
echo
echo "----COM Ports Devices--------"
# may want to keep the /dev/cua0 /dev/cua1 searches for older kernels
for SERPORT in /dev/ttyS0 /dev/ttyS1 
do
    if [ $UNDERTEST -eq 1 ]
    then
        cat ./setserial | grep ${SERPORT}
    else
        setserial -g ${SERPORT}
    fi
done

# rm temp files
CleanUp
echo
exit 0
