# /bin/sh
# $Copyright$
# Copyright (C) Fujitsu Technology Solutions 2009, 2010, 2011
# All rights reserved
#
#
# $Copyright$
# Copyright (C) Fujitsu Siemens Computers GmbH 2007, 2008, 2009
# All rights reserved
#
# $Id: meclear.sh,v 1.3 2009-01-16 10:20:31+01 richter Exp richter $
#
# NAME
#	meclear - Memory Module Error Counter Reset Utility
#
# SYNOPSIS
#	meclear [-T]
#
# DESCRIPTION
# 	This script allows to reset the error counts collected for
#	a memory module, for example after the memory module has been
#	replaced.
#
#	The utility presents the memory modules' state and interactively
#	asks the user for the number of the module whose error counts
#	shall be cleared.
#
# OPTIONS
#	None.
#
# ENVIRONMENT
#	If the env variable SRVMAGT_TEST is set to non-zero, a randomly
#	chosen module is indicated as having errors. No reset is attempted.
#	This is to test program logic and output presentation.
#
# SEE ALSO
#	eecdcp(8)
#	ServerControl Command Interface (SCCI) Specification
#
# EXIT STATUS
#	0 OK
#	1 Runtime Error
#
# CHANGE HISTORY
#	Jan 2007, -rit
#		Initial implementation as shell script

# ----------------------------------------------------------------------
# Constants
# ----------------------------------------------------------------------

MYSELF="${0##*/}"
MYLOGFILE=${MYLOGFILE:="/var/log/srvmagt/log.meclear"}
HASH="################"

state[0]="Not available"
state[1]="OK"
state[2]="disabled"
state[3]="error"
state[4]="fail"
state[5]="prefailure warning"
state[6]="hot-spare"
state[7]="mirror"
state[8]="raid"
state[9]="hidden"

# Array for module errors (dynamically extended)

errors[0]=

BANNER="
ServerView Linux Agents Memory Module Error Counter Reset
(c) Fujitsu Technology Solutions 2009"

CallingEecdcp="eecdcp: called with %s\n"
LeavingEecdcp="eecdcp: status %d, output '%s'\n"
LineFormat="%2d %-22s %-22s %-18s %8d\n"
Header="Nr Socket                 Bank                   Status               Errors\n"
InvalidAction="Invalid option"
MustBeBash="You must have the bash to execute this script. Exit!\n"
MustBeRoot="%s: You must be root to execute this program. Exit!\n"
ModulesFound="%d memory modules found.\n\n"
NoErrorsFound="All memory modules are free of errors.
A reset action is not necessary.\n"
NumberOfModule="Please enter the number 0 thru %d of the module to reset.\n[0-%d C=Cancel]> "
RejectedInvalidInput="Rejected! The input %s is undefined!\n"
RejectedInvalidNumber="Rejected! The module number %d is invalid!\n"
RejectedNoErrors="Rejected! The module %d has no errors!\n"
ResetOK="Resetting error count for memory module %d succeeded.\n"
ResetError="Resetting error count for memory module %d failed with status %d.\n"

# ----------------------------------------------------------------------
# General Transformations
# ----------------------------------------------------------------------

# xpairsTodecimal - transform 4 hexdigit pairs into a decimal number
#
# $1-4 		each argument is a hexdigit pair,
#			four adjacent pairs present a (32-bit) hexnumber in LSB.
# stdout	Present the transformed arguments as decimal number

function xpairsTodecimal() {
	local s=""

	[ "$#" -gt 0 ] || return
	let s=16#"$4$3$2$1"
	printf "$s"
}

# ----------------------------------------------------------------------
# Monolog: (Unconfirmed) output with logging
# ----------------------------------------------------------------------

function display() {
	printf "$(date +'%T') $@" >>$MYLOGFILE
	printf "$@"
}

function logonly() {
	printf "$(date +'%T') $@" >>$MYLOGFILE
}

# ----------------------------------------------------------------------
# eecd Access
# ----------------------------------------------------------------------

# eecdcpCmd - call eecdcp(8) with given arguments
#
# $1ff		additional arguments for eecdcp(8)
# output	command response (if any)
# Returns	0|1 if OK or not OK

EecdOutput=""

function eecdcpCmd() {
	local result output

	logonly "$CallingEecdcp" "$*"

	if [ $Toption -eq 0 ]; then
		EecdOutput=$(eecdcp -c "$@" 2>/dev/null)
		printf "$EecdOutput"

	else
		myEecdcp -c "$@"
	fi

	result=$?
	logonly "$LeavingEecdcp" "$?" "$EecdOutput"
	return $result
}

# <TEST>
# Simulate 4 configured memory modules soem of them with errors
#
# NOTE:
#	The bash does not modify global data within a function called via $()!

MyMemMods="04"								# NumberMemoryModules BYTE

MyMemMod0[1]="01"							# MemoryModuleStatus BYTE
MyMemMod0[2]="\"DIMM-1A (blue)\""			# MemoryModuleSocketDesignation	STRING
MyMemMod0[3]="\"ff ff ff ff 00 04 00 00 00 00 40 81 13 09 80 00\""	# MemoryModuleInfo BYTES
MyMemMod0[11]="00 00 00 00"					# StatsMemModCorrErrors DWORD
MyMemMod0[12]="00 00 00 00"					# StatsMemModUncorrErrors DWORD
MyMemMod0[42]="\"Node1/Bank1/MemCard1\""	# MemoryModuleBankDesignation STRING

MyMemMod1[1]="03"
MyMemMod1[2]="\"DIMM-2A (black)\""
MyMemMod1[3]="\"ff ff ff ff 00 04 00 00 01 00 40 81 13 09 80 00\""
MyMemMod1[11]="e7 4e bc 00"
MyMemMod1[12]="67 12 00 00"
MyMemMod1[42]="\"Node1/Bank1/MemCard2\""

MyMemMod2[1]="02"
MyMemMod2[2]="\"DIMM-1B (blue)\""
MyMemMod2[3]="\"ff ff ff ff 00 04 00 00 02 00 40 81 13 09 80 00\""
MyMemMod2[11]="00 00 00 00"
MyMemMod2[12]="00 00 00 00"
MyMemMod2[42]="\"Node1/Bank2/MemCard1\""

MyMemMod3[1]="05"
MyMemMod3[2]="\"DIMM-2b (blACK)\""
MyMemMod3[3]="\"ff ff ff ff 00 04 00 00 03 00 40 81 13 09 80 00\""
MyMemMod3[11]="aa 2f cb 03"
MyMemMod3[12]="00 00 00 00"
MyMemMod3[42]="\"Node1/Bank2/MemCard2\""

# myEecdcp - the eecdcp command for offline test witjout eecdcp present
#
# $@	individual eecdcp arguments, e.g.
#		oc=e001 oe=1001 oi=0 value
# Returns 	0.

function myEecdcp() {
	case $1 in
	-c)	case $2 in
		oc=0700) 							# CM_NumberMemoryModules
			printf "$MyMemMods"
			EecdOutput="$MyMemMods" ;;

		oc=0701|oc=0702|oc=0703|oc=0742)	# MemoryModuleStatus
			case $3 in
			oi=00)	printf "${MyMemMod0[${2#oc=07}]}" ; EecdOutput="${MyMemMod0[${2#oc=07}]}" ;;
			oi=01)	printf "${MyMemMod1[${2#oc=07}]}" ; EecdOutput="${MyMemMod1[${2#oc=07}]}" ;;
			oi=02)	printf "${MyMemMod2[${2#oc=07}]}" ; EecdOutput="${MyMemMod2[${2#oc=07}]}" ;;
			oi=03)	printf "${MyMemMod3[${2#oc=07}]}" ; EecdOutput="${MyMemMod3[${2#oc=07}]}" ;;
			*)	return 1 ;;
			esac ;;

		oc=0711)							# CM_ResetMemoryModuleErrorCount
			;;

		oc=e001)							# CM_ReadConfigurationSpace
			case $3 in
			oe=1001|oe=1002)				# StatsMemModCorrErrors
				case $4 in
				oi=00)	printf "${MyMemMod0[1${3#oe=100}]}" ; EecdOutput="${MyMemMod0[1${3#oe=100}]}" ;;
				oi=01)	printf "${MyMemMod1[1${3#oe=100}]}" ; EecdOutput="${MyMemMod1[1${3#oe=100}]}" ;;
				oi=02)	printf "${MyMemMod2[1${3#oe=100}]}" ; EecdOutput="${MyMemMod2[1${3#oe=100}]}" ;;
				oi=03)	printf "${MyMemMod3[1${3#oe=100}]}" ; EecdOutput="${MyMemMod3[1${3#oe=100}]}" ;;
				*)	return 1 ;;
				esac ;;
			esac ;;
		esac ;;

	*)	logonly "$InvalidAction" "$1" ;;
	esac
	return 0
}
# </TEST>

# ----------------------------------------------------------------------
# Main
# ----------------------------------------------------------------------

Toption=0

logonly "$(date +'%b %e %Y') %s %s %s $*\n" $HOSTNAME $HASH $MYSELF

# Make sure that a decent shell and path is used and root privileges apply

SRVMAGT=/etc/srvmagt
[ -f $SRVMAGT/messages/meclear_${LANG%_*}.msg ] && source $SRVMAGT/messages/meclear_${LANG%_*}.msg

PATH="/bin:/sbin:/usr/bin:/usr/sbin"

[ -n "${BASH}" ] || {
	printf "$MustBeBash"
	exit 2
}

[ $(id -un) = "root" ] || {
	printf "$MustBeRoot" $MYSELF
	exit 2
}

while getopts T optChar; do
	case "$optChar" in
	T)	Toption=1 ;;
	esac
done
shift $(($OPTIND - 1))

printf "$BANNER\n\n"

modules=$(eecdcpCmd oc=0700)	# Get number of memory modules
# AS IT WAS : modules=$((16#$modules))
modules=$(xpairsTodecimal $modules)

display "$ModulesFound" $modules
display "$Header"

totalErrs=0
for ((i=0; i < $modules; i++)); do
	oi=$(printf "%02x" $i)		  # eecdcp wants hex number args

	status=$(eecdcpCmd oc=0701 oi=$oi)		# Get memory module status
	socket=$(eecdcpCmd oc=0702 oi=$oi)		# Get memory module designation

	# info: ff ff ff ff 00 04 00 00 00 00 40 81 13 09 80 00
	#       start addr |module size|bank |module type

	info=$(eecdcpCmd oc=0703 oi=$oi)			# Get memory module info
	bn=${info:25:2}								# Evaluate only low byte of hex pair
	bank=$(eecdcpCmd oc=0742 oi=$bn)			# "Node1/Bank1/MemCard1"

	# 0x1001 CSV_StatsMemModCorrErrors		-> 00 00 00 00
	# 0x1002 CSV_StatsMemModUncorrErrors	-> 00 00 00 00

	corrErrs=$(eecdcpCmd oc=e001 oe=1001 oi=$oi 2> /dev/null) || corrErrs="00 00 00 00"
	uncorrErrs=$(eecdcpCmd oc=e001 oe=1002 oi=$oi 2> /dev/null) || uncorrErrs="00 00 00 00"

	corrErrs=$(xpairsTodecimal $corrErrs)
	uncorrErrs=$(xpairsTodecimal $uncorrErrs)

	totalErrs=$((totalErrs + corrErrs + uncorrErrs))
	errors[$i]=$((corrErrs + uncorrErrs + 0))

	display "$LineFormat" $i "$socket" "$bank" "${state[$(xpairsTodecimal $status)]}" ${errors[$i]}
done
printf "\n"

[ $totalErrs -eq 0 ] && {
	display "$NoErrorsFound"
	exit 0
}

# Do the reset on a module with errors. Prompt for the module number.
# Validate the module number and that the module HAS errors.

theModuleNr=""
while true; do
	printf "$NumberOfModule" \
		$((modules - 1)) $((modules - 1))
	read theModuleNr

	[ -z "$theModuleNr" ] && continue
	case "$theModuleNr" in
		[cC])
			exit 0 ;;
		[0-9]|[0-9][0-9])
			[ $theModuleNr -ge $modules ] && {
				printf "$RejectedInvalidNumber" $theModuleNr
				continue
			}

			[ ${errors[$theModuleNr]} -eq 0 ] && {
				printf "$RejectedNoErrors" $theModuleNr
				continue
			}
			break ;;
		*)  printf "$RejectedInvalidInput" $theModuleNr
			continue ;;
	esac
done

# Now fire the CM_ResetMemoryModuleErrorCount (0x0711) command for
# this memory module with errors and log the result

oi=$(printf "%02x" $theModuleNr)			  # eecdcp wants hex number args
eecdcpCmd oc=0711 oi=$oi
result=$?

# ----------------------------------------------------------------------
# Done
# ----------------------------------------------------------------------

if [ $result -eq 0 ]; then
	display "$ResetOK" $theModuleNr
	exit 0
else
	display "$ResetError" $theModuleNr $result
	exit 1
fi

# ----------------------------------------------------------------------
# EOF
