#!/bin/bash
#
# AUTHOR
#	Dennis Olsson (2007) -- Maintainer of the "sapinit" version 2 RPM package.
#	Dirk Hessing (2007) -- Initiator of the "sapinit" version 2 RPM package.
#	Wolfgang Rosenauer (2001-2006) -- Original author of the "sapinit" version 1 RPM package.
#	Holger Achtziger (1995-2001) -- Original author of the "suse-sapinit" RPM package.
#
# VERSION
#	$Id: SAPinit.sh,v 1.3 2007/05/09 18:29:48 dolsson Exp $
#
# COPYRIGHT
#	Copyright (c) 2007 SUSE Linux GmbH, Nuernberg, Germany.
#	Copyright (c) 2004-2006 SUSE Linux GmbH, Nuernberg, Germany.
#	Copyright (c) 2002-2004 SuSE Linux AG, Nuernberg, Germany.
#	Copyright (c) 1995-2001 SuSE GmbH, Nuernberg, Germany.
#	All rights reserved.
#	This product 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.
#
# LICENSE
#	The "SAPinit" script is used to automatically and dynamically to setup and verify various
#	kernel parameter settings upon initial system of the kernel, allowing a correct setup for
#	the running of a SAP system.
#
#	This program is released under the GPL Version 2, and is
#	Copyright (c) 2007 SUSE LINUX GmbH, Nuernberg, Germany, EU.
#	All rights reserved.
#
#	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, write to the Free Software
#	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

###########################################################  ##### #####
############# SYSTEM INITIALIZATION #######################  ##### ###  
###########################################################  ##### #    

set -o privileged		# Do not inherit functions
set -o nounset			# Raise error on expanding of unset variable
set -o errexit			# Exit on first error
set +o noglob			# Expand wildcards
set +o noclobber		# Overwrite files
set +o allexport		# DO NOT export all functions and variables
# set -o verbose		# List commands as they are read
# set -o xtrace			# List commands after parameter expansion

shopt -s expand_aliases		# Expand aliases, even if non interactive
shopt -s xpg_echo		# Turn on char escaping as default for "echo"
 

# Where is the script located (the true full path, please:)?
readonly PROGPATH="$(cd ${0%/*}; pwd -P)"

# Our name without any path and with full (true) path
readonly PROG="${0##*/}"
readonly PROGFULL="${PROGPATH}/${PROG}"

# Default exit value, if not overwritten either by having been set or through calling "_exit(<v>)"
typeset -i _exit=0

# Initialize the common variable to collect return status codes
typeset -i status=0

# Where to create temporary working files
readonly TMPDIR="/var/tmp"


# Function to create a new temporary file
# Do recall that *you* have to make sure that the file name is taken from TMP, should you be
# in need of more than one temporary file concurrently!!!
function CreateTMP()
{
    TMP="$(mktemp -p "${TMPDIR}" "${PROG}-XXXXXXXXXX")"
}


# Function used to exit from script
function _exit()
{
    [[ -n "${1:-}" ]] && _exit=${1}
    # Ignore catching of script ending
    trap EXIT

    # Clean up after our selfs, if need be (handle all cases of files and dirs)!
    local temp=$(echo "${TMPDIR}/${PROG}"*)
    [[ "${temp:$((${#temp}-1))}" != "*" ]] && rm -rf "${TMPDIR}/${PROG}"*

    # Exit with set error code
    exit ${_exit}
    # NEVER REACHED!
}
typeset -rf _exit

trap _exit EXIT 1 2 3 15


###########################################################  ##### #####
############# PREDEFINED VALUES ###########################  ##### ###  
###########################################################  ##### #    

# Setup some binary constants
typeset -ir KiB=1024			# 1 binaray kilo byte
typeset -ir MiB=$(( 1024*${KiB} ))	# 1 binaray mega byte
typeset -ir GiB=$(( 1024*${MiB} ))	# 1 binaray giga byte
typeset -ir TiB=$(( 1024*${GiB} ))	# 1 binaray tera byte
typeset -ir PiB=$(( 1024*${TiB} ))	# 1 binaray peta byte
typeset -ir EiB=$(( 1024*${PiB} ))	# 1 binaray exa byte
typeset -ir ZiB=$(( 1024*${EiB} ))	# 1 binaray zetta byte -- Not possible on 32bit, returns 0!

# Where to log found warnings and error messages
alias info='logger ${_stderr_:+${_stderr_} }-t "${PROG}" -p "daemon.info" -- INFO:'
alias warning='logger ${_stderr_:+${_stderr_} }-t "${PROG}" -p "daemon.warning" -- WARNING:'
alias error='logger ${_stderr_:+${_stderr_} }-t "${PROG}" -p "daemon.error" -- ERROR:'

# Used kernel configuration files
readonly MEMINFO="/proc/meminfo"
readonly  MOUNTS="/proc/mounts"
readonly  DEVSHM="/dev/shm"
readonly _DEVSHM="${DEVSHM//\//\\/}"	# As DEVSHM, but with the "/" as "\/" for RegExps.

# Which system control program to make use of
readonly SYSCTL="/sbin/sysctl"

# Where to find system and user configuration files
if [[ -z "${SAPinitDEBUG:-}" ]]
then
    # Where the mount points are specified
    readonly FSTAB="/etc/fstab"

    # Where the kernel parameters are specified
    readonly SYSCTLconf="/etc/sysctl.conf"

    # Where to find the user defined configuration parameters for this script
    readonly sysconfigSAPinit="/etc/sysconfig/${PROG}"

    # Where to find the DEVSHM size definition on SLES 9
    readonly sysconfigkernel="/etc/sysconfig/kernel"
    readonly SLES9SHMsize="SHMFS_SIZE"
else
    # Running in debug mode -- Use files out of current working directory
    readonly        DEBUGecho="echo"
    readonly            FSTAB="./etc/fstab"
    readonly       SYSCTLconf="./etc/sysctl.conf"
    readonly sysconfigSAPinit="./etc/sysconfig/${PROG%.sh}"
    readonly  sysconfigkernel="./etc/sysconfig/kernel"
    readonly     SLES9SHMsize="SHMFS_SIZE"
fi

# Should we inform about overwriting of recommended values (potential an option?-)?
readonly InfoReVOverwrt=true

# Should we inform about too small values being overwritten by usage of minimum values?
readonly InfoToSmall=true

# Should we inform about not having made any changes?
readonly InfoNoChange=true

# Should we only be logging to the syslog (this is definitively a potential option!-)?
#readonly OnlyToSyslog=true
readonly OnlyToSyslog=false


###########################################################  ##### #####
############# CONFIG ######################################  ##### ###  
###########################################################  ##### #    

# Calculate the current amount of available virtual memory as
#	TotalVirtMem	in number of bytes
#	TotalVirtMemMiB	in number of MiBytes
#	TotalVirtMemGiB	in number of GiBytes
# Values read from "/proc/meminfo" is in KiBytes units!
TotalVirtMem=(
	$(
	    awk -v KiB=${KiB} -v MiB=${MiB} '
		/(Mem|Swap)Total:/	{ mem += $2 }
		END		{ printf("%d %d %d\n", mem*KiB, int(mem/KiB), int(mem/MiB)) }
	    ' "${MEMINFO}"
	)
    )
typeset -ri TotalVirtMemGiB=${TotalVirtMem[2]}
typeset -ri TotalVirtMemMiB=${TotalVirtMem[1]}
typeset -i TotalVirtMem=${TotalVirtMem[0]}
readonly TotalVirtMem	# Bug in bash: typeset -ri VAR=EXP *first* marked RO, then assigned value!-(

# Figure out whether running on a 32 or 64 bit OS
B32=""
ARCH="$(uname -m)"
case "${ARCH}" in
    i?86)
	B32="32"
    ;;
    
    ia64 | x86_64 | ppc64 | s390x)	# 64 bit OS
	B32=""
    ;;

    *)
	warning "Unknown architecture: '${ARCH}' -- Treated as a 32 bit OS!"
    ;;
esac
readonly ARCH B32


###########################################################  ##### #####
### Start predefined values -- DO NOT REMOVE THIS LINE!! ##  ##### ###  
###########################################################  ##### #    

###########################################################  ##### #####
############# SHMFS #######################################  ##### ###  
###########################################################  ##### #    
# The minimum size of SHMFS (TMPFS in SAP Notes -- the TeMPorary SHared Memory mapping File System)
# is give for
#	64 bit systems in SAP Note 386605 with a value of 8 GiB
#	32 bit systems in SAP Note 386605 with a value of 2 GiB
typeset -ri SHMFSsize_MIN=8							# unit in GiB
typeset -ri SHMFSsize_MIN32=2							# unit in GiB

# The recommended size of SHMFS is give for
#	64 bit systems in SAP Note 386605 with a value of 75% of total available virtual memory
#	32 bit systems in SAP Note 386605 with a value of 75% of total available virtual memory
# Using a value of 0 (zero) for "*_ReV[32]" means use "*_MIN[32]".
typeset -ri SHMFSsize_ReV=$(( ${TotalVirtMemGiB}*75/100 ))			# unit in GiB
#typeset -ri SHMFSsize_ReV32= not defined means use 64 bit value instead	# unit in GiB

# Fetch the current size of the DEVSHM mounted directory, if mounted at all.
typeset -i SHMFSsize_Cur=-1							# unit in KiB!!
if grep -q "${DEVSHM}" "${MOUNTS}"
then
    # DEVSHM already mounted, fetch the current size expressed in KiBytes!!
    SHMFSsize_Cur=$(
	    df -k "${DEVSHM}" | awk "/${_DEVSHM}/ {print \$2}"
	)
fi
readonly SHMFSsize_Cur								# unit in KiB!!


###########################################################  ##### #####
############# SHMMAX ######################################  ##### ###  
###########################################################  ##### #    
# The minimum value for SHMMAX (maximum size of a shared memory segment -- units in bytes) is given
# for
#	64 bit systems in SAP Note <unknown> with an arbitrary minimum value
#	32 bit systems in SAP Note <unknown> with an arbitrary minimum value
# using arbitrary values, although experience has dictated these as being useful.
typeset -ri SHMMAX_MIN=$(( 1*${GiB} ))						# unit in bytes
#typeset -ri SHMMAX_MIN32= not defined means use 64 bit value instead		# unit in bytes

# The recommended value for SHMMAX is given for
#	64 bit systems in SAP Note 941735 with a value of 20 GiB
#	32 bit systems in SAP Note 386605 with a value of 2 GiB
# Using a value of 0 (zero) for "*_ReV[32]" means use "*_MIN[32]".
typeset -ri SHMMAX_ReV=$(( 20*${GiB} ))						# unit in bytes
typeset -ri SHMMAX_ReV32=$(( 2*${GiB} ))					# unit in bytes

# Make a note that it is possible to use "unlimited" (-1)
readonly SHMMAX_Unlimited=true

# Fetch the current value of SHMMAX in running system
readonly SHMMAX_sysctl="kernel.shmmax"
typeset -ri SHMMAX_Cur=$(${SYSCTL} -n "${SHMMAX_sysctl}")			# unit in bytes


###########################################################  ##### #####
############# SHMALL ######################################  ##### ###  
###########################################################  ##### #    
# The minimum value for SHMALL (total available amount of shared memory available -- units in 4 KiB
# blocks) is given for
#	64 bit systems in SAP Note 941735 with a value of at least the size of SHMMAX for 64 bits
#	32 bit systems in SAP Note 386605 with a value of at least the size of SHMMAX for 32 bits
typeset -ri SHMALL_MIN=$(( ${SHMMAX_ReV} / (4*${KiB}) ))		# unit in 4 KiB blocks
#typeset -ri SHMALL_MIN32= not defined means use 64 bit value instead	# unit in 4 KiB blocks

# The recommended value for SHMALL is given for
#	64 bit systems in SAP Note 941735 with a value equal to available virtual memory in system
#	32 bit systems in SAP Note <unknown> -- Assumes same value as for 64 bit systems
# Using a value of 0 (zero) for "*_ReV[32]" means use "*_MIN[32]".
typeset -ri SHMALL_ReV=$(( ${TotalVirtMem} / (4*${KiB}) ))		# unit in 4 KiB blocks
#typeset -ri SHMALL_ReV32= not defined means use 64 bit value instead	# unit in 4 KiB blocks

# Make a note that it is possible to use "unlimited" (-1)
readonly SHMALL_Unlimited=true

# Fetch the current value of SHMALL in running system
readonly SHMALL_sysctl="kernel.shmall"
typeset -ri SHMALL_Cur=$(${SYSCTL} -n "${SHMALL_sysctl}")		# unit in 4 KiB blocks


###########################################################  ##### #####
############# SEM{MNI,MSL,MNS,OPM} ########################  ##### ###  
###########################################################  ##### #    
# Minimal value for
#	SEMMSL	Maximum number of semaphores per identifier
#	SEMMNS	Maximum number of semaphores in system
#	SEMOPM	Maximum number of operations per semaphore call
#	SEMMNI	Maximum number of semaphore identifiers
# (the meaning of the constant names, see "/usr/src/linux/include/linux/sem.h") is given for
#	64 bit systems in SAP Note <unknown> with arbitrary minimum values
#	32 bit systems in SAP Note <unknown> with arbitrary minimum values
# using arbitrary values, although experience has dictated these as being useful.
typeset -ri SEMMSL_MIN=1250							# unit in numbers of
#typeset -ri SEMMSL_MIN32= not defined means use 64 bit value instead		# unit in numbers of
typeset -ri SEMMNS_MIN=256000							# unit in numbers of
#typeset -ri SEMMNS_MIN32= not defined means use 64 bit value instead		# unit in numbers of
typeset -ri SEMOPM_MIN=100							# unit in numbers of
#typeset -ri SEMOPM_MIN32= not defined means use 64 bit value instead		# unit in numbers of
typeset -ri SEMMNI_MIN=8192							# unit in numbers of
#typeset -ri SEMMNI_MIN32= not defined means use 64 bit value instead		# unit in numbers of

# The recommended value for:
#	SEMMSL	Maximum number of semaphores per identifier
#	SEMMNS	Maximum number of semaphores in system
#	SEMOPM	Maximum number of operations per semaphore call
#	SEMMNI	Maximum number of semaphore identifiers
# (for the meaning of the constant names, see "/usr/src/linux/include/linux/sem.h") is given for
#	64 bit systems in SAP Note <unknown> using defined minimum
#	32 bit systems in SAP Note <unknown> using defined minimum
# Using a value of 0 (zero) for "*_ReV[32]" means use "*_MIN[32]".
typeset -ri SEMMSL_ReV=0							# unit in numbers of
#typeset -ri SEMMSL_ReV32= not defined means use 64 bit value instead		# unit in numbers of
typeset -ri SEMMNS_ReV=0							# unit in numbers of
#typeset -ri SEMMNS_ReV32= not defined means use 64 bit value instead		# unit in numbers of
typeset -ri SEMOPM_ReV=0							# unit in numbers of
#typeset -ri SEMOPM_ReV32= not defined means use 64 bit value instead		# unit in numbers of
typeset -ri SEMMNI_ReV=0							# unit in numbers of
#typeset -ri SEMMNI_ReV32= not defined means use 64 bit value instead		# unit in numbers of

# Fetch the current values of SEM{MSL,MNS,OPM,MNI} in running system.
SEM_sysctl="kernel.sem"
readonly -a SEM_names=("SEMMSL" "SEMMNS" "SEMOPM" "SEMMNI")
typeset -a SEMvalues=( $(${SYSCTL} -n "${SEM_sysctl}") )
FatalError=false

set +o errexit				# Suspend: Exit on first error.
typeset -i i=0
while [[ ${i} -lt ${#SEM_names[*]} ]]
do
    if [[ -z "${SEMvalues[${i}]:-}" ]]
    then
	error "${PROGFULL} cannot fetch a current value for '${SEM_names[${i}]}' ==> Fatal Error!!"
	FatalError=true
    else
	typeset -ri ${SEM_names[${i}]}_Cur=${SEMvalues[${i}]}
	typeset -r  ${SEM_names[${i}]}_sysctl="${SEM_sysctl}"
	typeset -ri ${SEM_names[${i}]}_index=${i}
	typeset -ri ${SEM_names[${i}]}_MaxIndex=${#SEM_names[*]}
    fi
    let i++
done
set -o errexit				# Restore: Exit on first error.

if [[ ${i} -ne ${#SEMvalues[*]} ]]
then
    error "${PROGFULL} expects ${i} SEM values, but got ${#SEMvalues[*]} ==> Fatal Error!!"
    FatalError=true
fi

if ${FatalError}
then
    error \
	"Kindly report all previous listed Fatal Errors via http://Bugzilla.Novell.com/ -- Thanks!"
    _exit 2
    # NEVER REACHED!
fi
unset SEMparm FatalError i SEMvalues


###########################################################  ##### #####
############# MAX_MAP_COUNT ###############################  ##### ###  
###########################################################  ##### #    
# Minimal value for MAX_MAP_COUNT (maximum number of memory map areas a process may have) is given
# for
#	64 bit systems in SAP Note <unknown> with an unknown value
#	32 bit systems in SAP Note <unknown> with an unknown value
# and thus for both systems using some arbitrary value, which experience dictates as being useful.
#
# See "/usr/src/linux/Documentation/sysctl/vm.txt" for the definition of "vm.max_map_count".
typeset -ri MAX_MAP_COUNT_MIN=300000						# unit in numbers of
#typeset -ri MAX_MAP_COUNT_MIN32= not defined means use 64 bit value instead	# unit in numbers of

# The recommended value for MAX_MAP_COUNT is given for
#	64 bit systems in SAP Note <unknown> using defined minimum (= 0, mean use "*_MIN[32]")
#	32 bit systems in SAP Note <unknown> using defined minimum (= 0, mean use "*_MIN[32]")
# Using a value of 0 (zero) for "*_ReV[32]" means use "*_MIN[32]".
typeset -ri MAX_MAP_COUNT_ReV=0							# unit in numbers of
#typeset -ri MAX_MAP_COUNT_ReV32= not defined means use 64 bit value instead	# unit in numbers of

# Fetch the current used value of MAX_MAP_COUNT in running system
readonly MAX_MAP_COUNT_sysctl="vm.max_map_count"
typeset -ri MAX_MAP_COUNT_Cur=$(${SYSCTL} -n "${MAX_MAP_COUNT_sysctl}")		# unit in numbers of


###########################################################  ##### #####
#### End predefined values -- DO NOT REMOVE THIS LINE!! ###  ##### ###  
###########################################################  ##### #    


###########################################################  ##### #####
############# DECEODE OPTIONS PASSED ######################  ##### ###
###########################################################  ##### #

# Currently the script do not accept any options nor arguments!

# If having on a terminal, issue the syslog message on STDERR as well
tty -s && _stderr_="-s"

# If only going to log to the syslog, make sure that _stderr_ is unset!
${OnlyToSyslog} && unset _stderr_
readonly _stderr_


###########################################################  ##### #####
############# NEEDED SYSTEM FILES #########################  ##### ###
###########################################################  ##### #

# TODO: Is this really reasonable to do so?

# Is the FSTAB readable for us?
if [[ ! -r "${FSTAB}" ]]
then
    txt="is not readable"
    [[ ! -e "${FSTAB}" ]] && txt="does not exist"
    error "File '${FSTAB}' ${txt} -- Exiting!!"
    _exit 10
    # NEVER REACHED!
fi


###########################################################  ##### #####
############# USER DEFINED VALUES #########################  ##### ###
###########################################################  ##### #

# Fetch user defined parameter configuration, if available
if [[ -r "${sysconfigSAPinit}" ]]
then
    . "${sysconfigSAPinit}"
else
# TODO: Instead of barfing out if sysconfigSAPinit does not exist, make use of "*_ReV" values!
    txt="is not readable"
    [[ ! -e "${sysconfigSAPinit}" ]] && txt="does not exist"
    error "Configuration file '${sysconfigSAPinit}' ${txt} -- Exiting!!"
    _exit 11
    # NEVER REACHED!
fi


###########################################################  ##### #####
############# VALIDATION OF USER DEFINED PARAMETERS #######  ##### ###
###########################################################  ##### #

# Evaluate "*" (user defined) values and replace these with "*_ReV" (recommended) values, if the
# value of "*" is 0, and validate the values against "*_MIN[32]" values, and complain about any
# found inconsistencies.
ValidParms=""
for UserParm in $(sed -e '/^#/d;/^[^=]*=/!d;s/^\(.*\)=.*/\1/' "${sysconfigSAPinit}" | uniq)
do
    # Fetch the defined minimum parameter for the defined user parameter
    MIN="${UserParm}_MIN${B32}"
    [[ -z "${!MIN:-}" ]] && MIN="${UserParm}_MIN"	# Use 64 bit value, if 32 bit not defined

    # If no minimum parameter exists, we have a problem!
    NoMinimum=false
    [[ -z "${!MIN:-}" ]] && NoMinimum=true

    # Fetch the defined recommended parameter for the defined user parameter
    ReV="${UserParm}_ReV${B32}"
    [[ -z "${!ReV:-}" ]] && ReV="${UserParm}_ReV"	# Use 64 bit value, if 32 bit not defined

    # If no recommended parameter exists, we have a problem!
    NoRecommended=false
    [[ -z "${!ReV:-}" ]] && NoRecommended=true

    # Verify whether a parameter with a current system value exists -- If not, we have a problem!
    Cur="${UserParm}_Cur"
    NoCurrent=false
    [[ -z "${!Cur:-}" ]] && NoCurrent=true

    # If cases where the user has been using an unknown parameter name, info her about this, and
    # drop the parameter from further handling!
    if ${NoMinimum} || ${NoRecommended} || ${NoCurrent}
    then
	txt="User defined parameter name '${UserParm}' "
	if ${NoMinimum} && ${NoRecommended} && ${NoCurrent}
	then
	    txt="${txt}is an unknown parameter name to '${0}'"
	else
	    txt1=""
	    ${NoMinimum} && txt1="minimum"
	    ${NoRecommended} && txt1="${txt1:+${txt1} nor }recommended"
	    ${NoCurrent} && txt1="${txt1:+${txt1} nor }current value"
	    txt="${txt}does not have a corresponding ${txt1} parameter"
	fi
	error "${txt} -- Skipped!"
	continue
	# NEVER REACHED!
    fi

    # Should "*_MIN" be negative, complain and ignore, while "*_MIN" may not be negativ!
    if [[ ${!MIN} -lt 0 ]]
    then
	error "Parameter '${MIN}' may not be negative (${!MIN})" \
	    "=> Skipping parameter '${MIN%_*}' altogether!!"
	continue
	# NEVER REACHED!
    fi

    # Should "*_ReV" be less than -1, complain and ignore, while "*_ReV" may not be less then -1!
    if [[ ${!ReV} -lt -1 ]]
    then
	error "Parameter '${ReV}' may not be less then -1 (${!ReV})" \
	    "=> Skipping parameter '${ReV%_*}' altogether!!"
	continue
	# NEVER REACHED!
    fi

    # Fetch the value of the user defined parameter
    UserValue="${!UserParm}"

    # If user defined parameter has value 0, use recommended value
    [[ "${UserValue}" -eq 0 ]] && UserValue=${!ReV}
    # If the recommended value was 0 as well, use minimum value instead
    [[ "${UserValue}" -eq 0 ]] && UserValue=${!MIN}
    # Recall that -1 might be used to indicate "unlimited", and that the recommended value could
    # be "unlimited"!

    # If "unlimited" has not been explicit allowed, it is not possible to use -1 as "unlimited"!
    Unlimited="${UserParm}_Unlimited"

    # Verify that the user defined parameter has at least the minimum value, and if not set the
    # user defined parameter (which is going to be the used parameter) to the minimum value.
    # Recall that -1 means "unlimited", but ONLY if allowed!
    if ${!Unlimited:-false} && [[ "${UserValue}" -eq -1 ]]
    then
	info "An 'unlimited' value (${UserValue}) was specified for parameter '${UserParm}'."

    elif [[ "${UserValue}" -lt ${!MIN} ]]
    then
	if [[ "${UserValue}" -eq -1 ]]
	then
	    txt="The 'unlimited' (${UserValue}) value is not allowed for '${UserParm}'"
	    txt1="recommended"
	    UserValue=${!ReV}
	else
	    if [[ "${!UserParm}" -eq 0 ]]
	    then
		txt="Recommended value ${!ReV}"
	    else
		txt="User defined value ${!UserParm}"
	    fi
	    txt="${txt} for '${UserParm}' < minimum value ${!MIN}"
	    txt1="minimum"
	    UserValue=${!MIN}
	fi
	${InfoToSmall} && warning "${txt} ==> Using ${txt1} value!"
    
    elif ${InfoReVOverwrt} && [[ "${UserValue}" -eq ${!MIN} && ${!ReV} -eq 0 ]]
    then
	txt=""
	[[ "${UserParm}" == "SHMFSsize" ]] && txt="g"
	info "Recommended value for '${UserParm}' defaulted to used minimum value ${!MIN}${txt}."
    fi

    # Set the user defined value to the verified usable value found in the above verification
    eval typeset -i ${UserParm}=${UserValue}
    ValidParms="${ValidParms:+${ValidParms}\n}${UserParm}"
done

# ValidParms now contains all the valid parameter names and the values these should have
readonly ValidParms="$(echo "${ValidParms}" | sort)"


###########################################################  ##### #####
############# PREPARATION #################################  ##### ###
###########################################################  ##### #

# Prepare the notification texts used by this script, when updating configuration files
TimeFormat="+%Y-%m-%d %H:%M:%S %Z"
readonly Ident=" by ${PROGFULL} on $(date "${TimeFormat}") ($(date -u "${TimeFormat}"))."
readonly MarkerPattern="^# (Added|Modified) by .*${PROG} on "

# And generate a time stamp usable for file names
TimeFormat="${Ident##*(}"
readonly TimeStamp="$(date -d "${TimeFormat%%)*}" '+%Y%m%dT%H%M%S')"
unset TimeFormat


###########################################################  ##### #####
############# SLES version ################################  ##### ###  
###########################################################  ##### #    

# On which SLES version are we running?
# SLES9 == true using SLES 9 GM/SPx.
#       == false, using SLES 10 or higher.
if [[ -r "${sysconfigkernel}" ]]
then
    SLES9=$(
	    awk '
		/^[:blank:]*[^#]/ && /^'${SLES9SHMsize}'=/ { goit=1 }
		END { if (goit) print "true"; else print "false" }
	    ' "${sysconfigkernel}"
	)
else
    SLES9=false
fi
readonly SLES9


###########################################################  ##### #####
############# SHMFS #######################################  ##### ###  
###########################################################  ##### #    

# Function FSTABupdate handles the actual updating of the FSTAB file, when it has been determined
# that the FSTAB file needs to be updated.
#
# The function is parameter less -- All needed values are passed through global variables setup,
# before the call of the function.
#
# The function takes 1 parameter:
#
#	FSTABupdate <size>
#
# where
#
#	<size>		== "#"		Giving the size string used for DEVSHM being entered into
#					FSTAB.
#
# All other needed values are fetched from various global variables.
#
function FSTABupdate()
{
    local -r size="${1}"

    # More than one DEVSHM definition in FSTAB?
    if [[ ${DEVSHMentries} -gt 1 ]]
    then
	warning \
	    "'${FSTAB}' contains multiple specifications of '${DEVSHM}' (total ${DEVSHMentries})" \
	    "-- Only the first will be changed; The rest will be commented out."
    fi

    # In order to be able to handle FSTAB files of various contents, the following AWK program has
    # been designed to collect *all* DEVSHM references -- including those already out commented! --
    # and place them all at the end of the newly generated FSTAB.
    #
    # In the newly generated FSTAB only *one* entry will be active, and will have the wished for
    # size SHMFSsize setting of DEVSHM.
    #
    # The adding or changes will be headed by a indentifying line marking that this script has
    # changed the FSTAB file.
    CreateTMP
    awk \
	-v DEVSHM="${DEVSHM}" -v size="size=${size}" \
	-v Ident="${Ident}" -v MarkerPattern="${MarkerPattern}" \
	-f - -- "${FSTAB}" >"${TMP}" <<-"__EOF__"

	# If line does not starts with a comment (ignoring blanks!) & contains DEVSHM
	match(gensub("^[[:blank:]]+", "", "g"), "^[^#]") > 0 && match($0, DEVSHM) > 0 {
		    # Write out the previous read line, if it was not blank
		    if (delay && !blank) print line
		    blank = delay = 0

		    # Has the newly DEVSHM line for setting of SHMFSsize been defined?
		    if (shm[0] == "")
		    {
			# Nope, and the current line contains the DEVSHM definition
			# Already with the "size=" parameter?
			if ($0 ~ /size=/)
			{
			    # Yes, replace with new value
			    shm[0] = gensub("size=[0-9]+[kKmMgG]?", size, "g")
			} else {
			    # Nope, insert new size parameter at correct place
			    shm[0] = gensub("^(.*tmpfs[[:blank:]]+)(.*)$", "\\1" size ",\\2", "g")
			}
		    } else {
			# Yes, so the rest is just comment lines
			shm[++indx] = gensub("^", "#", "g")		# Make it a comment
		    }

		    # Stop any further processing, and continue with next input record!
		    next
		}

		# The output of lines up to the first occurrence of DEVSHM is being delayed with
		# one line, in order to be able to suppress the generation of unwanted blank lines
		# in the newly generate FSTAB file.
		{
		    # Skip previous header line(s) from this script
		    if ($0 ~ MarkerPattern) next

		    if (delay) print line
		    delay = 1
		    line = $0
		    blank = (length($0) == 0)
		}

	END	{
		    # Output any dangling read line
		    if (delay) print line

		    # Write out the header line from this script
		    printf("\n# ")
		    if (shm[0] == "")
		    {
			printf("Added")
			shm[0] = "shm\t\t\t" DEVSHM "\t\ttmpfs\t" size "\t\t\t\t\t0 0"
		    } else {
			printf("Modified")
		    }
		    print Ident

		    # And finish off with all the DEVSHM lines, starting with the newly set
		    # definition for DEVSHM using SHMFSsize as sizing parameter.
		    for (i = 0; i <= indx; i++) print shm[i]
		}
	__EOF__

    # Swap the new FSTAB and the OLD.
    # Rename the new FSTAB (in TMP) to FSTAB, backing up the old FSTAB in a time stamped backup.
    chmod 444 "${TMP}"
    set +o errexit				# Suspend: Exit on first error.
	mv -S "-${TimeStamp}" -bf "${TMP}" "${FSTAB}"
	status=$?
    set -o errexit				# Restore: Exit on first error.

    # Finish up
    if [[ ${status} -eq 0 ]]
    then
	info "Successfully updated '${FSTAB}' with '${DEVSHM}' using new size ${size}."
    else
	error "Failed to create new '${FSTAB}' -- *NO* changes done to '${FSTAB}' nor '${DEVSHM}'!!"
    fi

    return ${status}
}


# Function DEVSHMmounting handles the actual (re)mounting of DEVSHM in accordance with the
# specifications as these are found in FSTAB -- either predefined or as a result from an update
# via this script.
#
# The function takes 2 parameters:
#
#	DEVSHMmounting <action> <size>
#
# where
#	<action>	== "" | "remount"
#					Whether mounting ("") or remounting ("remount") DEVSHM.
#
#	<size>		== "#"		Giving the size string of the DEVSHM being (re)mounted.
#
# All other needed values are fetched from various global variables.
#
function DEVSHMmounting()
{
    local action="${1:-}" txt="'${DEVSHM}' with size ${2} from '${FSTAB}'."

    set +o errexit				# Suspend: Exit on first error.
	mount ${action:+-o ${action}} "${DEVSHM}"
	status=$?
	if [[ ${status} -eq 0 ]]
	then
	    info "Sucessfully ${action:-mount}ed ${txt}"
	else
	    error "** BE AWARE ** Was unable to ${action:-mount} ${txt}"
	    error "** BE AWARE ** The size parameter from '${FSTAB}' has NOT been activated!!"
	fi
    set -o errexit				# Restore: Exit on first error.
    
    return 0
}


# Has DEVSHM been specified in FSTAB at all (or even more than once)?
typeset -i DEVSHMentries=$(sed -e "/^[[:blank:]]*#/d;/${_DEVSHM}/!d" "${FSTAB}" | wc -l)

# Handle SLES version differences
if ${SLES9} && false
then
    # Any DEVSHM definition(s) in FSTAB?
    if [[ ${DEVSHMentries} -gt 0 ]]
    then
	error "'${FSTAB}' contains specification of '${DEVSHM}' (total ${DEVSHMentries})!!"
	txt="this entry has"; txt1="it has"
	[[ ${DEVSHMentries} -gt 1 ]] && { txt="these entries have"; txt1="they have"; }
	error "As ${txt} no effect, ${txt1} been commented out!!"
	error "The size of '${DEVSHM}' is controlled by '${SLES9SHMsize}' in '${sysconfigkernel}'!!"
	# TODO:
	# Comment out any DEVSHM definitions in FSTAB
	error "While currently NOT supporting SLES 9, the above has NOT been done!!"
    fi

    # TODO:
    # Here the SLES9SHMsize variable setting in the sysconfigkernel configuration file has to be
    # updated with the SHMFSsize value!

    # TODO:
    # Usually the DEVSHM has already been mounted, but even in cases, where this is not the case,
    # we still have to make sure that after executing this script, the DEVSHM has been mounted
    # using the SHMFSsize'ing!

    error \
	"Setting of '${SLES9SHMsize}' in '${sysconfigkernel}' on SLES 9" \
	"is current NOT supported by '${PROGFULL}' -- Sorry!!"
else
    # To begin with, we do not have any size spec for DEVSHM in FSTAB
    typeset -i FSTABsize=-1
    FSTABsizeOrig=""

    # If DEVSHM was specified in FSTAB, extract the "size=" parameter
    if [[ ${DEVSHMentries} -ge 1 ]]
    then
	# Fetch the line with the DEVSHM from FSTAB
	str="$(sed -e "/^[[:blank:]]*#/d;/${_DEVSHM}/!d;q" "${FSTAB}")"

	# Extract the value of the "size=" option
	FSTABsizeOrig="$(echo "${str}" | sed -e 's/^.*size=\([0-9]\+[kKmMgG]\?\).*$/\1/')"

	# Was there a number extracted?
	if [[ "${str}" != "${FSTABsizeOrig}" ]]
	then
	    # Yep, extract the multiplying factor, if any
	    str="${FSTABsizeOrig##*[0-9]}"

	    # and then the number
	    FSTABsize="${FSTABsizeOrig%%${str}}"

	    # Calculate the number in bytes depending on the multiplying factor
	    str="$(echo "${str}" | tr '[:lower:]' '[:upper:]')iB"
	    if [[ "${str}" == [KMG]iB ]]
	    then
		set +o errexit				# Suspend: Exit on first error.
		    let FSTABsize*=${!str}
		set -o errexit				# Restore: Exit on first error.
	    elif [[ -n "${str%%iB}" ]]
	    then
		error "Unexpected error occurred: Unknown multiplicator '${str%%iB}'" \
			"-- Kindly report via http://Bugzilla.Novell.com/ -- Thanks!"
		_exit 3
		# NEVER REACHED!
	    fi
	    set +o errexit				# Suspend: Exit on first error.
		let FSTABsize/=${KiB}
	    set -o errexit				# Restore: Exit on first error.
	fi
	unset str
    fi
    readonly FSTABsize			# unit in KiB!!
    readonly FSTABsizeOrig		# string -- the size specification from FSTAB

    # What to do?
    typeset -ri SHMFSsize_Wanted=$((${SHMFSsize}*${MiB}))	# Get the wanted size in KiB units!

    # Can the specified size in FSTAB as a minimum contain the wished for size of DEVSHM?
    remount=""
    if [[ ${FSTABsize} -ge ${SHMFSsize_Wanted} ]]
    then
	# Yes, but is it also the current active size?
	if [[ ${FSTABsize} -ne ${SHMFSsize_Cur} ]]
	then
	    # Ops, Nope, FSTAB entry does not match what is in use!
	    if [[ ${SHMFSsize_Cur} -eq -1 ]]
	    then
		txt="'${DEVSHM}' is not currently mounted although defined in '${FSTAB}'"
	    else
		txt="'${DEVSHM}' size ${SHMFSsize_Cur}k != size ${FSTABsizeOrig} as in '${FSTAB}'"
		remount="remount"
	    fi
	    warning "${txt}."
	    DEVSHMmounting "${remount}" "${FSTABsizeOrig}"
	fi
    else
	# Nope, FSTAB needs to be updated.
	unset txt
	size="${SHMFSsize}g"
	DoMounting=true

	# Figure out under which conditions we are updating the FSTAB, and info user about this
	txt="'${DEVSHM}' "
	if [[ ${FSTABsize} -eq -1 ]]
	then
	    # FSTAB does not contain any DEVSHM, but is DEVSHM already mounted?
	    if [[ ${SHMFSsize_Cur} -ge 0 ]]
	    then
		# Yupe, DEVSHM is mounted, but does it have the right size?
		txt="${txt} is mounted with size ${SHMFSsize_Cur}k"
		if [[ ${SHMFSsize_Cur} -ge ${SHMFSsize_Wanted} ]]
		then
		    # Yupe => Only update FSTAB
		    DoMounting=false
		    size="${SHMFSsize_Cur}k"
		    txt="${txt} >= wanted ${SHMFSsize}g, but"
		else
		    # Nope => Update FSTAB and remounted DEVSHM
		    remount="remount"
		    txt="${txt} < wanted ${SHMFSsize}g and is"
		fi
		txt="${txt} not specified in '${FSTAB}'"
	    else
		# Nope, DEVSHM not mounted => Update FSTAB and mount DEVSHM
		txt="${txt} using size ${SHMFSsize}g not specified in '${FSTAB}'"
	    fi
	else
	    # FSTAB has a DEVSHM specification, but is it already mounted?
	    if [[ ${SHMFSsize_Cur} -eq -1 ]]
	    then
		# Nope => Update FSTAB and mount DEVSHM
		txt="${txt} currently not mounted and specified in '${FSTAB}'"
		txt="${txt} with size ${FSTABsizeOrig} < wanted ${SHMFSsize}g"
	    else
		# Nope => Update FSTAB and remount DEVSHM
		remount="remount"
		txt="${txt} mounted from '${FSTAB}' size ${SHMFSsize_Cur}k < wanted ${SHMFSsize}g"
	    fi
	fi
	txt="${txt} -- Updating '${FSTAB}'"
	${DoMounting} && txt="${txt} and activating entry"

	# Right -- inform user, and update FSTAB
	warning "${txt}."
	FSTABupdate "${size}" && ${DoMounting} && DEVSHMmounting "${remount}" "${size}"
    fi
fi


###########################################################  ##### #####
############# SYSCTL PARAMETERS ###########################  ##### ###  
###########################################################  ##### #    

# Function SysctlConfUpdate handles the updating of the SYSCTLconf file, in accordance with the
# passed on parameters and the current contents of the SYSCTLconf file.
#
# The function takes 4 parameters:
#
#	SysctlConfUpdate <entries> <AddEntry> <sysctl> <UpdateValue> <index>
#
# where
#  $1	<entries>	== #	Of entries of <sysctl> found in the SYSCTLconf file.
#
#  $2	<AddEntry>	== false	Existing <sysctl> entry in SYSCTLconf has to be modified.
#
#  $3	<sysctl>	== <string>	Full SYSCTLconf parameter string name of parameter that has
#					to be updated or modified.
#
#  $4	<values>	== # -or-	The value the SYSCTlconf parameter name should be assigned.
#			   array of #s
#
#  $5	<index>		== # > -1	Index number of the SYSCTLconf parameter name value that
#					has to be updated, in cases where the parameter is an array.
#			== -1		No index number (SYSCTLconf parameter name is not an array).
#
# All other needed values are fetched from various global variables.
#
function SysctlConfUpdate()
{
    local -ri entries=${1}
    local -r AddEntry="${2}" sysctl="${3}" value="${4}"

    # Found more than one sysctl definition in SYSCTLconf?
    if [[ ${entries} -gt 1 ]]
    then
	warning \
	    "'${SYSCTLconf}' contains multiple specifications of '${sysctl}' (total ${entries})" \
	    "-- Only the last entry will be effective!"
	    # TODO:
	    # "-- Only the last entry will be changed; The rest will be commented out."
    fi

    if ${AddEntry}
    then
	# Add the entry to the SYSCTLconf file
	echo "\n# Added ${Ident}\n${sysctl} = ${value}" >>"${SYSCTLtmp}"
    else
	# Modify the entry to the SYSCTLconf file
	echo "\n# Modified ${Ident}\n${sysctl} = ${value}" >>"${SYSCTLtmp}"
    fi

    return 0
}


# Function CreateSYSCTLconf handles the creation resp. renaming of the SYSCTLconf file based on
# the SYSCTLtmp file containing the new kernel parameters.
#
# The function takes 2 parameter:
#
#	CreateSYSCTLconf <action> <endtext>
#
# where
#  $1	<action>	== ""		The SYSCTLconf file is being created for the first time.
#			== "update"	An existing SYSCTLconf is being updated with new values.
#			== "noupdate"	Cannot update the SYSCTLconf file, while it was changed
#					under us.
#
#  $2	<endtext>	== ""		Use a default end text for action "noupdate".
#			== <text>	Use passed <text> as end text for action "noupdate".
#
# All other needed values are fetched from various global variables.
#
function CreateSYSCTLconf()
{
    local -r action="${1:-}" EndTxt="${2:-based on the previous contents of '${SYSCTLconf}'!}"

    # Rename the new SYSCTLtmp to SYSCTlconf using a time stamped backup of the old SYSCTLconf file.
    chmod 444 "${SYSCTLtmp}"
    set +o errexit				# Suspend: Exit on first error.
	if [[ "${action}" != "noupdate" ]]
	then
	    mv -S "-${TimeStamp}" -bf "${SYSCTLtmp}" "${SYSCTLconf}"
	    status=$?
	else
	    mv "${SYSCTLtmp}" "${SYSCTLconf}-${TimeStamp}" 
	    status=$?
	fi
    set -o errexit				# Restore: Exit on first error.

    # Finish up
    if [[ ${status} -eq 0 ]]
    then
	case "${action}" in
	    "")
		info "Created new '${SYSCTLconf}' with the new kernel parameter values."
		info "Activating the new kernel parameters from '${SYSCTLconf}'."
		${SYSCTL} -p "${SYSCTLconf}"
	    ;;

	    update)
		info "Successfully updated '${SYSCTLconf}' with newest kernel parameter values."
		info "Activating the new kernel parameters from '${SYSCTLconf}'."
		${SYSCTL} -p "${SYSCTLconf}"
	    ;;

	    noupdate)
		warning "The '${SYSCTLconf}' was changed during the run of this command!"
		warning \
		    "The file '${SYSCTLconf}-${TimeStamp}' contains the updated kernel" \
		    "parameters${EndTxt}"
		warning "The kernel parameters have NOT been changed!!"
	    ;;

	    *)
		error "in PROGRAMMING: CreateSYSCTLconf '${action}' => Unknown action -- Aborting!!"
		_exit 239
	    ;;
	esac
    else
	error "Failed to create new '${SYSCTLconf}' -- *NO* changes done to '${SYSCTLconf}'!!"
    fi

    return 0
}


# Figure out whether the kernel parameters need to be reconfigured by running through each and
# everyone of them, comparing these with the values in SYSCTLconf as well as the currently used
# kernel values, and perform any necessary updates.

# If not already done, create a working copy of the SYSCTLconf file
if [[ -z "${TMP:-}" || ! -w "${TMP}" ]]
then
    CreateTMP
    readonly SYSCTLtmp="${TMP}" SYSCTLtmpmd5="${TMP}-md5"

    # Does the SYSCTLconf file exist at all?
    if [[ -r "${SYSCTLconf}" ]]
    then
	# Yupe, copy it, and calculate the md5sum of it
	cp "${SYSCTLconf}" "${SYSCTLtmp}"
	( umask 077; md5sum <"${SYSCTLtmp}" >"${SYSCTLtmpmd5}"; chmod 400 "${SYSCTLtmpmd5}"; )
    else
	# Nope, make sure we do not have an md5sum file
	rm -f "${SYSCTLtmpmd5}"
    fi
fi

# Short hand for white space pattern
readonly s="[[:blank:]]*"

# Whether the SYSCTLconf parameter values have to be activated at all -- Updating SYSCTLconf
# automagically implies the activation of the SYSCTLconf values.
NeedActivation=false

# The "SYSCTLvalue" array contains the values from the SYSCTLconf file parameters independed on
# whether the value is a single integer or an array of integers.
typeset -ai SYSCTLvalue=( -1 )

for Parm in ${ValidParms}
do
    # Reset working variables
    unset UpdateSysctl UpdateValue AddEntry ParmIndex
    typeset -i entries=-1 index=0 sysctlconf=-1 UpdateValue

    # Only parameters that have a "*_sysctl" are of interest to us here
    sysctl="${Parm}_sysctl"
    [[ -z "${!sysctl:-}" ]] && continue

    # Initialize the SYSCTLvalue array accordingly to the current parameter specifications
    SYSCTLvalue=( -1 )
    ParmIndex="${Parm}_MaxIndex"
    index=${!ParmIndex:-0}
    while [[ ${index} -gt 0 ]]
    do
	let index--
	SYSCTLvalue[${index}]=-1
    done

    # Yep, fetch the value in the SYSCTLconf file, if any...
    if [[ -r "${SYSCTLtmp}" ]]
    then
	# File is there -- Do we have an entry for the parameter of interest?
	entries=$(sed -e "/^${s}#/d;/^${s}${!sysctl}${s}=/!d" "${SYSCTLtmp}" | wc -l)
	if [[ ${entries} -ge 1 ]]
	then
	    # Yupe, fetch the last line with the sysctl of interest in SYSCTLconf
	    SYSCTLvalue=(
		    $(
			tac "${SYSCTLtmp}" |
			sed -e "/^${s}#/d;/^${s}${!sysctl}${s}=/!d;s/^.*=${s}//;q"
		    )
		)
	fi
    fi
    # Setup value of sysctl parameter read from SYSCTLconf file, if any
    ParmIndex="${Parm}_index"
    index=${!ParmIndex:-0}
    sysctlconf=${SYSCTLvalue[${index}]}

    # Is the specified value in SYSCTLconf greater than what is wanted?
    txt=""
    Cur="${Parm}_Cur"
    if [[ ${sysctlconf} -ge ${!Parm} ]]
    then
	# Yep, but it is also the current used value?
	UpdateSysctl=false
	if [[ ${sysctlconf} -gt ${!Cur} ]]
	then
	    txt="In '${SYSCTLconf}': ${!sysctl} = ${sysctlconf} > current in use ${!Cur}"

	elif [[ ${sysctlconf} -lt ${!Cur} ]]
	then
	    txt="In '${SYSCTLconf}': ${!sysctl} = ${sysctlconf} > wanted ${!Parm}"
	    txt="${txt}, but is < current in use ${!Cur}"
	fi
	if [[ -n "${txt}" ]]
	then
	    NeedActivation=true
	    txt="${txt} -- '${SYSCTLconf}' value will be activated!"
	fi
    else
	# Nope, SYSCTLconf have to be updated.
	UpdateSysctl=true

	if [[ ${sysctlconf} -eq -1 ]]
	then
	    AddEntry=true
	    # SYSCTLconf does not contain any entry for sysctl, but is the current used value OK?
	    if [[ ${!Cur} -ge ${!Parm} ]]
	    then
		# Yupe, use current used value
		txt="Current value for ${!sysctl} = ${!Cur} >= wanted ${!Parm}"
		UpdateValue=${!Cur}
	    else
		# Nope, use wanted value
		txt="Wanted value for ${!sysctl} = ${!Parm} > current ${!Cur}"
		UpdateValue=${!Parm}
	    fi
	    txt="${txt}, but has not been entered into '${SYSCTLconf}' -- Adding entry!"
	else
	    AddEntry=false
	    # SYSCTLconf contains an entry for sysctl and it too small for our needs.
	    # But, what is the current used value?
	    if [[ ${!Cur} -ge ${!Parm} ]]
	    then
		txt="Current value for ${!sysctl} = ${!Cur} >= wanted ${!Parm}"
		UpdateValue=${!Cur}
	    else
		txt="Wanted value for ${!sysctl} = ${!Parm} > current ${!Cur}"
		UpdateValue=${!Parm}
	    fi
	    txt="${txt} >= ${sysctlconf} in '${SYSCTLconf}' -- Updating entry!"
	fi
    fi

    # Issue warning, if one was generated
    [[ -n "${txt:-}" ]] && warning "${txt}"

    # Do the updating, if needed
    if ${UpdateSysctl}
    then
	SYSCTLvalue[${index}]=${UpdateValue}
	SysctlConfUpdate ${entries} ${AddEntry} "${!sysctl}" "${SYSCTLvalue[*]}"
    fi
done

# If the SYSCTLtmp file is available, we just might have an updated SYSCTLconf to activate
if [[ -r "${SYSCTLtmp}" ]]
then
    # First, was there an SYSCTLconf file at all to begin with?
    if [[ -r "${SYSCTLtmpmd5}" ]]
    then
	# Yupe, did it change during our work?
	if md5sum -c --status "${SYSCTLtmpmd5}" <"${SYSCTLconf}"
	then
	    # Nope -- did we make any changes?
	    if cmp -s "${SYSCTLconf}" "${SYSCTLtmp}"
	    then
		${InfoNoChange} && tty -s &&
		    info "No changes were made to the '${SYSCTLconf}' file."

		if ${NeedActivation}
		then
		    info "Activating '${SYSCTLconf}' kernel parameters as current values are lower."
		    ${SYSCTL} -p "${SYSCTLconf}"
		fi
	    else
		# Yupe, update the SYSCTLconf file.
		CreateSYSCTLconf update
	    fi
	else
	    # Yupe!
	    # Ops -- not good -- Cannot update file, while the original has been changed under us!
	    # Place the updated SYSCTLconf in a new file, and inform user about the problem!!
	    CreateSYSCTLconf noupdate
	fi

    elif [[ -r "${SYSCTLconf}" ]]
    then
	# Nope!
	# Ops -- not good -- Cannot overwrite file with the new kernel parameters, while a new
	# SYSCTLconf file was created during our run!
	# Place the newly created SYSCTLconf in a new file, and inform user about the problem!!
	CreateSYSCTLconf noupdate "."

    else
	# Nope, which means we did not have any SYSCTLconf to begin with -- Create the new file.
	CreateSYSCTLconf
    fi
fi

exit 0
# NEVER REACHED!

###########################################################  ##### #####
############# RLIMITS #####################################  ##### ###  
###########################################################  ##### #    

# TODO:
# rlimits for nofiles
#*    hard    nofile    65536
#*    soft    nofile    65536


###########################################################  ##### #####
############# DEBUG #######################################  ##### ###  
###########################################################  ##### #    

set +x
###########################################################  ##### #####
# Print "*_(MIN|ReV)[32]" parameters and their values
if false
then
    readonly MINReVPARMS="$(
	    set 2>&1 |
	    sed -e '/^[^=]*_\(MIN\|ReV\)\(32\)\?=/!d;s/^\(.*_\(MIN\|ReV\)\(32\)\?\)=.*/\1/' | uniq
	)"
    echo "\n_(MIN|ReV)[32] prefixed parameter:"
    for var in ${MINReVPARMS}
    do
	printf "%-20s =>" "${var}"
	typeset -p "${var}"
    done
    #exit
fi
###########################################################  ##### #####

###########################################################  ##### #####
# Print "*_Cur" parameters and their values
if false
then
    readonly CurPARMS="$(
	    set 2>&1 | sed -e '/^[^=]*_Cur=/!d;s/^\(.*_Cur\)=.*/\1/' | uniq
	)"
    echo "\n_Cur prefixed parameter:"
    for var in ${CurPARMS}
    do
	printf "%-20s =>" "${var}"
	typeset -p "${var}"
    done
fi
###########################################################  ##### #####

###########################################################  ##### #####
# Print all valid defined user parameters and their values
if false
then
    echo "\nAll valid defined parameter with should be values:"
    for var in ${ValidParms}
    do
	printf "%-15s => %s" "${var}" "$(typeset -p "${var}")"
	sys="${var}_sysctl"
	if [[ -n "${!sys:-}" ]]
	then
	    echo " with ${sys} = ${!sys}\c"
	    parms="${sys%_*}_index"
	    Mparms="${sys%_*}_MaxIndex"
	    [[ "${!parms:-}" ]] && echo "[${!parms} with max ${!Mparms}]\c"
	fi
	echo
    done
fi
###########################################################  ##### #####
#set -x
