#!/bin/bash
#
# Copyright (c) 2013-2023 University of Utah and the Flux Group.
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file 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 Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#

_1KiB=1
_1KMiB=1024
_256KMiB=262144
_1KGiB=1048576
_16KGiB=16777216
_32KGiB=33554432
_64KGiB=67108864

# these number are in MB
_1Mib=1
_1GiB=1024
_2GiB=2024
_4GiB=4096
_8GiB=8192
_32GiB=32768    # 2G close enough
_64GiB=65536    # 4G close enough
_128Gib=131072  # 8G close enough 
_256Gib=262144  # 8G close enough 
#_bigger        # ...

echo -n ' Starting Memcheck..'

source checkutils.sh
source hbis.sh

x=$(caller)
[[ "${x/NULL}" = "$x" ]] && declare -ri memcheck_standalone=0 || declare -ri memcheck_standalone=1

declare failed="" s=""

initialize $@
cp /dev/null $tmplog

finish() {
    echo -n "Memcheck `date`: " >> ${logfile}
    cat ${tmplog} >> ${logfile} 
    
    echo "$failed"
    echo "$failed" >> ${logfile} 
    return 0
}

s=""

# check that externally need programs are installed
case $os in
    Linux )
	if (( $mfsmode )) ; then
	    progs="cat grep"
	else
	    progs="cat grep bc"
	fi
	;;
    FreeBSD )
	progs="grep cat"
	;;
    * )
	failed="Unknown OS :$os:"
	finish
	(( $memcheck_standalone )) && exit 1 || return 1
	;;
esac
for i in $progs ; do
    type $i &>/dev/null && continue  || s="$s $i "
done
if [ -n "$s" ] ; then
    failed=" Unable to run need missing command(s) $s"
    echo "$failed" >> ${tmplog}
    finish
    (( $memcheck_standalone )) && exit 0 || return 0
fi

{
case $os in
    Linux )
	type dmidecode &>/dev/null && DMIEXE=$(which dmidecode) || DMIEXE=""
	if [ -n "$DMIEXE" ] ; then
	    memmethod="dmidecode"  # save method using for later output
	    # get the Memory Array Mapped Addresses
	    $DMIEXE -t 19 > /tmp/dmi19.out
	    grep "Range Size:" /tmp/dmi19.out > /tmp/dmi19x.out
	    sed 's/Range Size: //g' /tmp/dmi19x.out > /tmp/dmi19.out
	    # we assume that all sizes given in the same units
	    size_unit=""
	    while read  -r in ; do # get size unit
		line=$in
		size_unit=${line//[0-9]/} # remove numbers
	    done < /tmp/dmi19.out
	    # count the size
	    size_total=0
	    while read -r in ; do # add up the numbers in the file
		line=$in
		line=${in//[a-z]*/} # remove letters
		line=${in//[A-Z]*/}
		((size_total+=$line))
	    done < /tmp/dmi19.out
	    # run it through our favorite routine
	    meminfo=$(hbis "$size_total $size_unit")

	    # use dmidecode to count and add up DIMMs, this is just infor for full file
	    $DMIEXE -t 17 > /tmp/dmi17.out
	    grep $'\t'Size: /tmp/dmi17.out > /tmp/dmi17x.out
	    DIMMslots=$(wc -l < /tmp/dmi17x.out)
	    DIMMsempty=$(grep "No Module" /tmp/dmi17x.out | wc -l)
	    grep -v "No Module" /tmp/dmi17x.out > /tmp/dmi17.out
	    x=$(head -1 /tmp/dmi17.out)
	    DIMMsize=${x//Size: /}
	else
	    unset -v d ; declare -a d=($(cat /proc/meminfo | grep MemTotal))
	    memmethod="/proc/meminfo"
	    # we don't have the use of bc in mfs/load boot
	    if (( $collect_flag )) ; then
		# punt punt punt
		number=${d[1]}
		if (( number > $_64KGiB )) ; then
		    ((number+=51200))
		elif (( number > (($_32KGiB + $_16KGiB)) )); then
		    ((number+=1024000))
		elif (( number > $_32KGiB )) ; then
		    ((number+=2048000))
		elif (( number > $_1KGiB )) ; then
		    ((number+=31457))
		elif (( number > $_256KMiB )) ; then
		    ((number+=47972))
		elif (( number > $_1KMiB )) ; then
		    ((number+=48))
		fi
	    else
	    # Linux under reports, add ??MiB (in KiB units) for reserved mem
		if (( ${d[1]} > $_32KGiB )) ; then
		    number=$(echo "scale=0; (${d[1]} * 1.015) / 1;" | bc) # divide by 1 for the truncate
		elif (( ${d[1]} > $_1KGiB )) ; then
		    number=$(echo "scale=0; (${d[1]} * 1.03) / 1;" | bc)
		elif (( ${d[1]} > $_256KMiB )) ; then
		    number=$(echo "scale=0; (${d[1]} * 1.183) / 1;" | bc)
		elif (( ${d[1]} > $_1KMiB )) ; then
		    number=$(echo "scale=0; (${d[1]} * 1.047) / 1;" | bc)
		else 
		    number=$(echo "scale=0; (${d[1]} * 1.0) / 1;" | bc)
		fi
	    fi
	    mi=${number}${d[2]}
	    meminfo=$(hbis $mi)
	fi
	    ;;
    FreeBSD )
	    unset -v d ; declare -a d=($(grep memory /var/run/dmesg.boot | grep real))
	    # ???? use the bytes and not the stuff in the (), seem to round up using int rounding.
	    # example: "real memory  = 268419072 (255 MB)" is not true, 268419072 = 255.984375 MB
#	    w=${d[4]} ; x=${w#(}
#		z=${d[5]} ; y=${z%)}
#	    meminfo=$x$y
	    meminfo=${d[3]}
	    meminfo=$(hbis $meminfo)
	    ;;
    * )
	    echo "os $os unknown"
	    meminfo="0"
	    failed=FAIL
	    ;;
esac

echo -n "Found phys memory $meminfo"
} >> ${tmplog} 2>&1

# Is found memory 'close enough' to value from tmcc hwinfo
tbinfo=$(getfromtb MEMINFO)

if [ $tbinfo -eq $meminfo ] ; then : ; fi  # equal, great!
slop=$(($tbinfo - $meminfo))
if [ $tbinfo -ge $_128Gib ] ; then # guess DIMM size 8G
    if [ $slop -lt $_8GiB ] ; then
	meminfo=$tbinfo
    fi
elif [ $tbinfo -ge $_64GiB ] ; then # guess DIMM size 4G
    if [ $slop -lt $_4GiB ] ; then
	meminfo=$tbinfo
    fi
elif [ $tbinfo -ge $_32GiB ] ; then # guess DIMM size 2G
    if [ $slop -lt $_2GiB ] ; then
	meminfo=$tbinfo
    fi
fi

# if saving data for testbed database
if (( $collect_flag )) ; then
    printf "MEMINFO SIZE=%s\n" ${meminfo} >> ${logfile4tb}
fi

# we are done if in MFS mode
if (( $mfsmode )) ; then
    (( $memcheck_standalone )) && exit 0 || return 0
fi

# else running in experiment, output something for the user
{
if [ "$meminfo" = "$tbinfo" ] ; then
    echo " Equals node inventory $tbinfo"
else
    echo " Does not equals node inventory $tbinfo"    
    failed=FAIL
fi
} >> ${tmplog} 2>&1

echo -n "Memcheck `date`: " >> ${logfile}
cat ${tmplog} >> ${logfile}

[[ -z ${failed} ]] && echo "$meminfo" || echo "TBmiss Have |$meminfo| Want |$tbinfo| FAILED"

(( $memcheck_standalone )) && exit 0 || return 0
