#! /bin/ksh -p
#
#ident "@(#)sccheck_common.ksh 1.10     03/03/21 SMI"
#
# Copyright (c) 2003 Sun Microsystems, Inc.
# All rights reserved.
# Use is subject to license terms.
#

#
# functions and global variables
# common to sccheck.ksh and sccheckd.ksh
#


export LC_COLLATE=C

PATH=/usr/cluster/bin:${PWD}:/bin:/usr/bin:/sbin:/usr/sbin:${PATH}; export PATH

#
#	Definitions and functions common
#	to both sccheck and sccheckd.
#


#
#	Configuration file.
#
# In a non-inetd environment the user would just export variables from their
# environment and run sccheck. However, sccheckd also needs access to
# 'user overrides' which inetd cannot pass along.
# Therefore, 'user environment overrides' may be set in this file which
# is accessible to both sccheck and sccheckd.
#
# The most common use of this file will be to override default locations
# of utilities like explorer, java, and gunzip.
#
# This file also holds various properties used by the java code and
# is expected to be present,although its absence will not be fatal.
#
typeset SCCHECK_CONF=/etc/default/sccheck

if [[ -e ${SCCHECK_CONF} ]]; then
    . ${SCCHECK_CONF}
fi


#
#	definitions common to client & server
#


integer -r SC_TRUE=1
integer -r SC_FALSE=0
typeset -r TRUE="true"   # no I18N
typeset -r FALSE="false" # no I18N

typeset -r CLINFO=/usr/sbin/clinfo
typeset -r SCHA_CLGET="/usr/cluster/bin/scha_cluster_get -O"
typeset -r SCCONF="/usr/cluster/bin/scconf -p"

# user can override via sccheck config file
typeset EXPLORER_HOME=${EXPLORER_HOME:-/opt/SUNWexplo}
typeset -r EXPLORER=${EXPLORER_HOME}/bin/explorer

# user can override via sccheck config file
#   default #1
typeset GUNZIP=${GUNZIP:-/usr/bin/gunzip}
if [[ ! -x ${GUNZIP} ]]; then
    #   default #2
    GUNZIP=/opt/SUNWexplo/bin/gzip.`/usr/bin/uname -p`
fi

typeset TAR=${TAR:-/usr/bin/tar}

typeset MYNAME=$(uname -n)

# user can override via sccheck config file
typeset JAVA_HOME=${JAVA_HOME:-/usr/java}	

# user can override via sccheck config file
typeset JAVA=${JAVA:-${JAVA_HOME}/bin/java}


typeset -r SCCHECK_LIB=/usr/cluster/lib/sccheck
typeset -r SCCHECK_JAR=${SCCHECK_LIB}/sccheck.jar

#
# KE:  Knowledge Engine
#   aka
# KAE: Knowledge Automation Engine
#   aka
# EKE: Embedded Knowledge Engine
#
typeset -r KE_HOME=${SCCHECK_LIB}/kae


# having SCCHECK_LIB in the path with SCCHECK_JAR
# allows finding I18N resource bundles outside the jar
typeset CLASSPATH=${SCCHECK_LIB}:${SCCHECK_JAR}

# add KE jars to classpath
set -A KE_RESOURCES "\
	kae.jar \
	kae-libs.jar \
	eras-common.jar \
	common-libs.jar \
	resources\
"

for resource in ${KE_RESOURCES}
do
	CLASSPATH=${CLASSPATH}:${KE_HOME}/${resource}
done


# suffix for default output directory name
typeset -r TIMESTAMP="$(date +%Y-%m-%d.%T)"


#
# space in MB required for one copy of explorer results compressed
# _and_ uncompressed
# small systems won't generate this much; big systems may generate more
# user can override from environment
#
typeset -i SCCHECK_DISKSPACEMB=${SCCHECK_DISKSPACEMB:-25} 

#
#	exit code definitions
#
#	not documented to users
#	these specific codes used by testing
#

integer -r ERR_UNSPECIFIED=201
integer -r ERR_TRAP=202
integer -r ERR_USAGE=203
integer -r ERR_NOT_ROOT=204
integer -r ERR_DISK_SPACE=205
integer -r ERR_FAILED_DIRS=206
integer -r ERR_PREREQS=207
integer -r ERR_EXPL_PARENT=208


#
#	filename definitions
#

typeset -r SCCHECK_LOGDIR="/var/cluster/logs/sccheck"
typeset -r KELOG=ke.log

typeset -r SCCHECK_REPORTSDIR_SERVER="/var/cluster/sccheck/tmp/server"
typeset -r SCCHECK_REPORTSDIR_CLIENT="/var/cluster/sccheck/reports."${TIMESTAMP}

typeset -r SCCHECK_PIDFILES_BASE=${SCCHECK_LOGDIR}


#
#	java defines for Knowledge Engine
#

set -A KE_JDEFINES " \
	-DconfigSuffix=erassandbox \
	-Dkae.kce.KCEFactFactory.KCEre=${KE_HOME}/storage/facts.zip \
	-Dkae.Explorer.LocalDB=/tmp \
	-Dkae.kce.KCEFactFactory.useDBStore=false \
	-Dkae.useRMI=false \
	-Dlogging.handlers=FileHandler \
	-Dlogging.FileHandler.level=ALL \
	-Dlogging.FileHandler.path=${SCCHECK_LOGDIR}/${KELOG} \
"


#
#	function definitions
#
#


###################################
#
#  common_check_prereqs
#
#	input:		nothing
#	output:		error messages
#	returns:	0 success or 1 failure
#
#	action: 
#
#	Check for prerequisite utilities and data
#	files.
#
###################################

common_check_prereqs()
{
    typeset -i prereq=0

    typeset -r ewrapper=${SCCHECK_LIB}/explorer_wrapper
    typeset -r singlexml=${SCCHECK_LIB}/checklist.cluster.singlenode.xml
    typeset -r multixml=${SCCHECK_LIB}/checklist.cluster.multinode.xml
    typeset -r nonclxml=${SCCHECK_LIB}/checklist.noncluster.xml


    set -A exec_files "\
	${EXPLORER} \
	${GUNZIP} \
	${TAR} \
	${JAVA} \
	${ewrapper} \
    "

    set -A exist_files "\
	${singlexml} \
	${multixml} \
	${nonclxml} \
    "

    for f in ${exec_files}
    do
	if [[ ! -x ${f} ]]; then
	    printf "$(gettext '%s: %s not found or not executable.')\n" ${PROG} "${f}"
	    prereq=1
	fi
    done

    for f in ${exist_files}
    do
	if [[ ! -e ${f} ]]; then
	printf "$(gettext '%s: %s not found.')\n" ${PROG} "${f}"
	    prereq=1
	fi
    done

    return ${prereq}
} # common_check_prereqs

##############################
#
#  common_get_explorer_version
#
#	Return version string from explorer(1m).
#
#	input:		nothing
#	output:		print version string from explorer(1m)
#	returns:	nothing
#
#	action:
#
#	Ask explorer for its version string.
#	First use new-style flag, if it fails
#	try old-style flag.
#
#	no I18N: use C locale no make sure
#
##############################

common_get_explorer_version ()
{
    typeset lc_save=${LC_ALL}
    export LC_ALL=C

    typeset exv=$(${EXPLORER} -version 2>&1)
    typeset expl_version=

    # expr chokes on the output of this version call to explorer
    #	expr $("${exv}") : "Explorer version"
    # so we're left with echo | grep
    echo "$exv" | /usr/bin/grep "Explorer version" > /dev/null
    if [[ $? -eq 0 ]]; then
	expl_version=$(echo "$exv" | /usr/bin/grep "Explorer version")
    else
	exv=$(${EXPLORER} -V 2>&1)
	echo "$exv" | /usr/bin/grep "Explorer version" > /dev/null
	if [[ $? -eq 0 ]]; then
	    expl_version=$(echo "$exv" | /usr/bin/grep "Explorer version")
	else
	    expl_version="undetermined"	# no I18N
	fi
    fi

    # strip off the leading "Explorer version: " & convert spaces to _
    echo ${expl_version##*: } | /usr/bin/tr "[:blank:]" "_"

    export LC_ALL=${lc_save}
} # common_get_explorer_version

##############################
#
#  common_get_java_version
#
#	Obtain version string from java
#
#	input:		nothing
#	output:		Version string from JVM
#	returns:	nothing
#
#	action: 
#
#	Ask JVM for its version; translate
#	double quotes into <space>.
#
#	no I18N: use C locale
#
##############################
common_get_java_version ()
{

    typeset lc_save=${LC_ALL}
    export LC_ALL=C

    # get version string and replace double
    # quotes with <space>
    typeset jver=$($JAVA -version 2>&1 | /usr/bin/tr "\"" " " )

    echo ${jver}

    export LC_ALL=${lc_save}
} # common_get_java_version

##############################
#
# common_get_clustermode
#
#	input:		nothing
#	output:		prints ${TRUE} | ${FALSE}
#	returns:	nothing
#
#	action: 
#
#	Use clinfo to see if we're in clustermode.
#
##############################

common_get_clustermode()
{ 
    if [[ -x ${CLINFO} ]]; then
	${CLINFO} > /dev/null 2>&1
	if [[ $? -eq 0 ]]; then
	    echo ${TRUE}
	else
	    echo ${FALSE}
	fi
    else
	echo ${FALSE}
    fi
} # common_get_clustermode

###############################################
#
# common_get_clustername
#
#	input:		nothing
#	output:		prints:
#		clustername if in clustermode,
#		nothing if not in clustermode
#		or ifscha command not found
#
#	returns:	nothing
#
#	action: 
#
#	Use scha_clget() to fetch clustername.
#
###############################################
common_get_clustername()
{
    echo $(${SCHA_CLGET} CLUSTERNAME 2> /dev/null)
}

###############################################
#
# common_mkdir()
#
#	input:		name of directory to create
#	output:		prints error message
#	returns:	0 success || 1 failure
#
#	action: 
#
#	Attempt to mkdir -p the target directory.
#
######################################################
common_mkdir()
{
    typeset target=$1

    /usr/bin/mkdir -p ${target} 2> /dev/null
    integer status=$?
    if [[ ${status} -ne 0 ]]; then
	printf "$(gettext '%s:  Could not create directory: %s')\n" ${PROG} "${target}"
	return 1
    fi

    return 0
} # common_mkdir

########################################################
#
# common_mkdirs
#
#	input:		nothing
#	output:		prints error message	
#	returns:	0 success || 1 failure
#
#	action: 
#
#	Make directories needed on both client
#	and server.
#
########################################################

common_mkdirs()
{
    common_mkdir ${SCCHECK_REPORTSDIR_SERVER}	|| return 1
    common_mkdir ${SCCHECK_LOGDIR}		|| return 1

    return 0
} # common_mkdirs


#####################################################
#
# common_test_diskspace
#
#	Determine if enough disk space is available to
#	store explorer results. This is a total estimate
#	since explorer results sizes can vary widely from
#	machine to machine.
#
#	This is why env var $SCCHECK_DISKSPACEMB may be
#	overridden in the user's environment.
#
#	A server machine need only hold one explorer results
#	set, but client machine needs to hold number of
#	participating nodes plus one explorer results.
#
#	input:		N: multiple of space required
#			defaults to 1
#
#	output:		error message
#	returns:	0 if sufficient space or 1 if not
#
#	action: 
#
#	Get available space in / filesystem in KB. Convert to bytes.
#	Convert $SCCHECK_DISKSPACEMB to bytes, then multiply by
#	'N' to get total space required.
#	Compare total bytes required against bytes available.
#
#####################################################

common_test_diskspace()
{
    typeset -i factor=1
    typeset -i availkb=0
    typeset -i avail=0
    typeset -i min_node_diskspace=0
    typeset -i totrqd=0

    if [[ $# -gt 0 ]]; then
	factor=$1
    fi

    availkb=$(/usr/bin/df -k / | /bin/tail -1 | /usr/bin/awk '{print $4}')
    ((avail=$availkb * 1024))

    ((min_node_diskspace=$SCCHECK_DISKSPACEMB * 1024 * 1024))

    ((totrqd=$min_node_diskspace * $factor))

    if (( $avail >= $totrqd )); then
	return 0
    else
	printf "$(gettext '%s:  Insufficient disk space.')\n" ${PROG}
	return 1
    fi
} # common_test_diskspace

########################################################
#
# common_clean_pidfiles
#
#	Validate that there is an active sccheck(d)
#	process corresponding to each pidfile.
#
#	Both sccheck.ksh and sccheckd.ksh create
#	a 'pidfile' holding their process ID before
#	they invoke their java code and remove it on exit.
#
#	This validation function is extra insurance
#	against a stale pidfile remaining to confuse
#	the logic that depends upon the presence/absence
#	of pidfiles.
#
#	input:		pidfile 'base' name 
#	output:		nothing
#	returns:	nothing
#
#	action:
#
#	For all pidfiles matching pidfile 'base' name, read
#	the contained pid and test the environment to see
#	if that process exists and has a name that includes
#	the string 'sccheck.' This  will match both 'sccheck'
#	and 'sccheckd.'
#
#	Any pidfile containg an 'invalid' pid is removed.
#	
########################################################

common_clean_pidfiles()
{
    typeset lc_save=${LC_ALL}
    export LC_ALL=C

    typeset target=$1
    typeset pidfiles=$(/usr/bin/ls -1 ${target}* 2>/dev/null)
    typeset pidf=
    for pidf in $(echo $pidfiles)
    do
	typeset contentpid=$(/usr/bin/cat $pidf)

	# the typeset of and assignment to $pss must be separate
	# operations in order to evaluate the success of the grep
	typeset pss=

	# see if ${contentpid} matches an existing sccheck or
	# sccheckd process
	pss=$(/usr/bin/pgrep "sccheck" | /usr/bin/grep -w ${contentpid})
	typeset status=$?
	if [ $status -ne 0 ]; then
	    /usr/bin/rm $pidf 2> /dev/null
	fi
    done   
    export LC_ALL=${lc_save}
} # common_clean_pidfiles


#####################################################
#
# common_verify_isroot()
#
#	Print an error message and return non-zero
#	if the user is not root.
#
#####################################################

common_verify_isroot()
{
	typeset -r uid=$(expr "$(id)" : 'uid=\([0-9]*\)*')

	# make sure uid was set
	if [[ -z "${uid}" ]]; then
		printf "$(gettext '%s:  Cannot get uid')\n" ${PROG}
		return 1
	fi

	# check for root
	if [[ ${uid} -ne 0 ]]; then
		printf "$(gettext '%s:  Must be root')\n" "${PROG}"
		return 1
	fi

	return 0
} # common_verify_isroot

#####################################################
#
# common_verify_isauthorized()
#
#       Print an error message and return non-zero
#       if the user is not authorized.
#
#####################################################

common_verify_isauthorized()
{
        /usr/bin/auths 2>&1 | /usr/bin/egrep "solaris\.cluster\.system\.admin|solaris\.cluster\.system\.\*|solaris\.cluster\.\*|solaris\.\*" > /dev/null

        if [ $? -ne 0 ] ; then
                printf "$(gettext '%s:  Not authorized to use this command.')\n" ${PROG}
                return 1
        fi

        return 0
} # common_verify_isauthorized

#####################################################
#
# common_verify_iseuidroot()
#
#	Print an error message and return non-zero
#	if the euid of the process is not 0.
#
#####################################################

common_verify_iseuidroot()
{
	typeset -r euid=`/usr/xpg4/bin/id -u`

	# make sure euid was set
	if [[ -z "${euid}" ]]; then
		printf "$(gettext '%s:  Cannot get euid')\n" ${PROG}
		return 1
	fi

	# check for root
	if [[ ${euid} -ne 0 ]]; then
		printf "$(gettext '%s:  Must be root')\n" "${PROG}"
		return 1
	fi

	return 0
} # common_verify_iseuidroot
