#!/bin/bash
# Description: Displays basic system information
# Modified:    2014 Aug 14

##############################################################################
#  Copyright (C) 2014 SUSE LLC
##############################################################################
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; version 2 of the License.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, see <http://www.gnu.org/licenses/>.
#
#  Authors/Contributors:
#     Jason Record (jrecord@suse.com)
#
##############################################################################

##############################################################################
# Variables
##############################################################################
SVER=1.0.12
PAD=25
QUIET=0
FORMAT_OUT="%-${PAD}s %s\n"

if [[ -s /etc/hostinforc ]]
then
	. /etc/hostinforc
else
	echo "Error: Missing /etc/hostinforc"
	exit 2
fi

##############################################################################
# Functions
##############################################################################
showHelp()
{
	echo "hostinfo - Gathers host information"
	echo
	echo "USAGE: hostinfo [OPTIONS]"
	echo "OPTIONS"
	echo " -h This help screen"
	echo " -n Ignore network address output"
	echo " -m Ignore memory output"
	echo " -d Ignore disk output"
	echo " -u Ignore updates output"
	echo " -q Quiet mode"
	echo " -v View the current root-motd"
	echo
}

exit_code() {
	XC=$1
	shift
	MSG="$@"
	[[ -n "$MSG" ]] && echo "$MSG"
	exit $XC
}

showHostname()
{
	if (( QUIET ))
	then
		printf "$FORMAT_OUT" "Hostname:" "$(hostname)" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" "Hostname:" "$(hostname)" | tee -a $FILE_ROOT_MOTD
	fi
}

hrLine()
{
	if (( QUIET ))
	then
		echo '-----------------------------------------------------------' >> $FILE_ROOT_MOTD
	else
		echo '-----------------------------------------------------------' | tee -a $FILE_ROOT_MOTD
	fi
}

emptyLine()
{
	if (( QUIET ))
	then
		echo >> $FILE_ROOT_MOTD
	else
		echo | tee -a $FILE_ROOT_MOTD
	fi
}

mask2cdr ()
{
	local ones=${1##*255.}
	set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#ones})*2 )) ${ones%%.*}
	ones=${1%%$3*}
	echo $(( $2 + (${#ones}/4) ))
}

showDate()
{
	if (( QUIET ))
	then
		printf "$FORMAT_OUT" "Current As Of:" "$(date '+%c')" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" "Current As Of:" "$(date '+%c')" | tee -a $FILE_ROOT_MOTD
	fi
}

showDistribution()
{
	if (( QUIET ))
	then
		printf "$FORMAT_OUT" "Distribution:" "$(cat /etc/SuSE-release | grep SUSE | cut -d'(' -f1)" >> $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" " -Service Pack:" "$(cat /etc/SuSE-release | grep PATCHLEVEL | awk '{print $3}')" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" "Distribution:" "$(cat /etc/SuSE-release | grep SUSE | cut -d'(' -f1)" | tee -a $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" " -Service Pack:" "$(cat /etc/SuSE-release | grep PATCHLEVEL | awk '{print $3}')" | tee -a $FILE_ROOT_MOTD
	fi
}

showKernel()
{
	KERNEL_STR=$(uname -r)
	KERN_RPM="kernel-$(echo $KERNEL_STR | awk -F- '{print $NF}')"
	KERN_DATE=$(rpm -q --queryformat "%{INSTALLTIME:date}" $KERN_RPM 2>/dev/null)
	if [[ -n "$KERN_DATE" ]]; then
		KERN_DATE_STR="$KERN_DATE"
	else
		KERN_DATE_STR="Unknown"
	fi
	TAINT=$(cat /proc/sys/kernel/tainted)
	TAINTED=0
	if [ ${#TAINT} -gt 1 ]; then
		TAINTED=1
	elif [ ${TAINT} -gt 0 ]; then
		TAINTED=1
	fi
	if (( TAINTED )); then
		# TAINT=$(( 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 ))
		# Refer to /usr/src/linux/include/linux/kernel.h and /usr/src/linux/kernel/panic.c (print_tainted function)
		TAINT_STRING=""
		test $((TAINT & 1))          -ne 0 && TAINT_STRING="${TAINT_STRING}P" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 2))          -ne 0 && TAINT_STRING="${TAINT_STRING}F" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 4))          -ne 0 && TAINT_STRING="${TAINT_STRING}S" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 8))          -ne 0 && TAINT_STRING="${TAINT_STRING}R" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 16))         -ne 0 && TAINT_STRING="${TAINT_STRING}M" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 32))         -ne 0 && TAINT_STRING="${TAINT_STRING}B" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 64))         -ne 0 && TAINT_STRING="${TAINT_STRING}U" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 128))        -ne 0 && TAINT_STRING="${TAINT_STRING}D" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 256))        -ne 0 && TAINT_STRING="${TAINT_STRING}A" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 512))        -ne 0 && TAINT_STRING="${TAINT_STRING}W" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 1024))       -ne 0 && TAINT_STRING="${TAINT_STRING}C" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 1073741824)) -ne 0 && TAINT_STRING="${TAINT_STRING}N" || TAINT_STRING="${TAINT_STRING} "
		test $((TAINT & 2147483648)) -ne 0 && TAINT_STRING="${TAINT_STRING}X" || TAINT_STRING="${TAINT_STRING} "
		KERN_TAINT_STR="Tainted: $TAINT_STRING"
	else
		KERN_TAINT_STR="Not Tainted"
	fi
	if grep -i "suse linux enterprise" /etc/SuSE-release | grep 12 &>/dev/null
	then
		SHOW_TAINT=1
	else
		SHOW_TAINT=0
	fi

	if (( QUIET ))
	then
		printf "$FORMAT_OUT" "Architecture:" "$(uname -i)" >> $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" "Kernel Version:" "$KERNEL_STR" >> $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" " -Installed:" "$KERN_DATE_STR" >> $FILE_ROOT_MOTD
		(( SHOW_TAINT )) && printf "$FORMAT_OUT" " -Status:" "$KERN_TAINT_STR" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" "Architecture:" "$(uname -i)" | tee -a $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" "Kernel Version:" "$KERNEL_STR" | tee -a $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" " -Installed:" "$KERN_DATE_STR" | tee -a $FILE_ROOT_MOTD
		(( SHOW_TAINT )) && printf "$FORMAT_OUT" " -Status:" "$KERN_TAINT_STR" | tee -a $FILE_ROOT_MOTD
	fi
}

showNetAddr()
{
	NET_LIST=$(ifconfig -a)
	STATE=1
	FIRST=1
	IFACE=''
	IADDR=''
	SLAVE=0
	echo "$NET_LIST" | while read LINE
	do
		#echo "STATE=$STATE FIRST=$FIRST IFACE=$IFACE $LINE"
		if (( STATE ))
		then
			if (( FIRST ))
			then
				IFACE=$(echo $LINE | awk '{print $1}')
				FIRST=0
				[[ "$IFACE" == "lo" ]] && STATE=0
			elif echo "$LINE" | grep "^inet addr:" &>/dev/null
			then
				IADDR=$(echo $LINE | cut -d' ' -f2 | cut -d\: -f2)
				IMASK=$(echo $LINE | awk '{print $NF}' | cut -d\: -f2)
				CIDR=$(mask2cdr $IMASK)
			elif echo "$LINE" | grep SLAVE &>/dev/null
			then
				SLAVE=1
			elif echo "$LINE" | grep "TX packet" &>/dev/null
			then
				if [[ -n "$IADDR" ]]
				then
					IPADDR_STR="$IFACE ${IADDR}/${CIDR}"
				else
					if (( SLAVE ))
					then
						IPADDR_STR="$IFACE (Bond Slave)"
					else
						IPADDR_STR="$IFACE (Unconfigured)"
					fi
				fi
				IFACE=''
				IADDR=''
				STATE=0
				FIRST=0
				if (( QUIET ))
				then
					printf "$FORMAT_OUT" "IPv4 Address:" "$IPADDR_STR" >> $FILE_ROOT_MOTD
				else
					printf "$FORMAT_OUT" "IPv4 Address:" "$IPADDR_STR" | tee -a $FILE_ROOT_MOTD
				fi
			fi
		elif [[ -z "$LINE" ]]
		then
			STATE=1
			FIRST=1
			SLAVE=0
		fi
	done
}

showUpdates()
{
	LIP_ALL=$(rpm -qa --last | head -1)
	LIP_NAME=$(echo $LIP_ALL | cut -d' ' -f1)
	LIP_DATE=$(echo $LIP_ALL | sed -e "s/${LIP_NAME}[[:space:]]*//g")
	if (( QUIET ))
	then
		printf "$FORMAT_OUT" "Last Updated Package:" "$LIP_DATE" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" "Last Updated Package:" "$LIP_DATE" | tee -a $FILE_ROOT_MOTD
	fi

	REPOS=$(zypper --non-interactive --no-gpg-checks repos 2>/dev/null | grep '^[[:digit:]]' | wc -l)
	if (( REPOS > 1 ))
	then
		PATCH_STRING=$(zypper --non-interactive --no-gpg-checks patch-check 2>/dev/null | tail -1 | grep -i patches 2>/dev/null)
		if [[ -n "$PATCH_STRING" ]]
		then
			PATCH_COUNT_NEEDED=$(echo $PATCH_STRING | cut -d' ' -f1)
			PATCH_COUNT_SECURITY=$(echo $PATCH_STRING | cut -d\( -f2 | cut -d' ' -f1)
		else
			PATCH_COUNT_NEEDED='Pending'
			PATCH_COUNT_SECURITY='Pending'
		fi
	else
		PATCH_COUNT_NEEDED='Not Registered'
		PATCH_COUNT_SECURITY='Not Registered'
	fi
	if (( QUIET ))
	then
		printf "$FORMAT_OUT" " -Patches Needed:" "$PATCH_COUNT_NEEDED" >> $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" " -Security:" "$PATCH_COUNT_SECURITY" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" " -Patches Needed:" "$PATCH_COUNT_NEEDED" | tee -a $FILE_ROOT_MOTD
		printf "$FORMAT_OUT" " -Security:" "$PATCH_COUNT_SECURITY" | tee -a $FILE_ROOT_MOTD
	fi

	COUNT_NVR=$(rpm -qa --queryformat "%{VENDOR}|%{NAME}\n" | egrep -iv "^SUSE LLC|^SUSE Linux Enterprise|gpg-pubkey$" | wc -l)
	if (( QUIET ))
	then
		printf "$FORMAT_OUT" " -3rd Party Packages:" "$COUNT_NVR" >> $FILE_ROOT_MOTD
	else
		printf "$FORMAT_OUT" " -3rd Party Packages:" "$COUNT_NVR" | tee -a $FILE_ROOT_MOTD
	fi
}

showMemory()
{
	MEM_TOTAL=$(cat /proc/meminfo | grep MemTotal: | awk '{printf "%i", $2/1024}')
	MEM_FREE=$(cat /proc/meminfo | grep MemFree: | awk '{printf "%i", $2/1024}')
	if [[ -x /usr/bin/free ]]
	then
		MEM_CACHE=$(/usr/bin/free -k | grep '^-' | awk '{printf "%i", $NF/1024}')
		MEM_PERCENT=$(printf "%i" $(( MEM_CACHE * 100 / MEM_TOTAL )))
		if (( QUIET ))
		then
			printf "$FORMAT_OUT" "Total/Free/+Cache Memory:" "${MEM_TOTAL}/${MEM_FREE}/${MEM_CACHE} MB (${MEM_PERCENT}% Free)" >> $FILE_ROOT_MOTD
		else
			printf "$FORMAT_OUT" "Total/Free/+Cache Memory:" "${MEM_TOTAL}/${MEM_FREE}/${MEM_CACHE} MB (${MEM_PERCENT}% Free)" | tee -a $FILE_ROOT_MOTD
		fi
	else
		if (( QUIET ))
		then
			printf "$FORMAT_OUT" "Total/Free Memory:" "${MEM_TOTAL}/${MEM_FREE} MB" >> $FILE_ROOT_MOTD
		else
			printf "$FORMAT_OUT" "Total/Free Memory:" "${MEM_TOTAL}/${MEM_FREE} MB" | tee -a $FILE_ROOT_MOTD
		fi
	fi
}

showDisks()
{
	DISK_SIZES=$(egrep '[s,h,xv,c][0-9]?d[a-z,0-9]$' /proc/partitions | awk '{printf "%s %u %s\n", "/dev/"$4, $3*1024/1000^3, "GB"}')
	if [ -z "$DISK_SIZES" ]; then
		DISK_SIZES=Unknown
	fi
	echo "$DISK_SIZES" | while read DISK_INFO
	do
		THIS_DISK=$(echo $DISK_INFO | awk '{printf "%s %u %s\n", $1, $2, $3}')
		if (( QUIET ))
		then
			printf "$FORMAT_OUT" "Hard Disk:" "$THIS_DISK" >> $FILE_ROOT_MOTD
		else
			printf "$FORMAT_OUT" "Hard Disk:" "$THIS_DISK" | tee -a $FILE_ROOT_MOTD
		fi
	done
}

displayCurrentDate()
{
	if ! (( QUIET ))
	then
		echo
		printf "$FORMAT_OUT" "Current Date:" "$(date '+%c')"
	fi
}

showRootMotd()
{
	if [[ -s $FILE_ROOT_MOTD ]]
	then
		if grep "Hostname:" $FILE_ROOT_MOTD &> /dev/null
		then
			displayCurrentDate
			DATE_NEEDED=0
			cat $FILE_ROOT_MOTD
			exit 0
		fi
	fi
}

##############################################################################
# main
##############################################################################
DATE_NEEDED=1
while getopts :hnmdquv TMPOPT
do
	case $TMPOPT in
	\:)	showHelp
			case $OPTARG in
			*) echo "ERROR: Missing Argument -$OPTARG"
				;;
			esac
			echo; exit 1 ;;
	\?)	showHelp
			case $OPTARG in
			*) echo "ERROR: Invalid Option -$OPTARG"
				;;
			esac
			echo; exit 1 ;;
	n) IGNORE_NET=1 ;;
	m) IGNORE_MEM=1 ;;
	d) IGNORE_DISK=1 ;;
	u) IGNORE_UPDATES=1 ;;
	h) showHelp; exit 0 ;;
	q) QUIET=1 ;;
	v) showRootMotd ;;
	esac
done

(( DATE_NEEDED )) && displayCurrentDate
> $FILE_ROOT_MOTD
hrLine
showHostname
showDate
showDistribution
showKernel
(( IGNORE_UPDATES )) || showUpdates
(( IGNORE_NET )) || showNetAddr
(( IGNORE_MEM )) || showMemory
(( IGNORE_DISK )) || showDisks
hrLine
emptyLine

