#!/bin/ksh93 
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r720 src/43haes/usr/sbin/cluster/cspoc/cdsh.sh 1.16.1.3 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 1996,2015 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)05        1.16.1.3 src/43haes/usr/sbin/cluster/cspoc/cdsh.sh, hacmp.cspoc, 61haes_r720, 1538A_hacmp720 9/4/15 14:29:33

#================================================
# The following, commented line enforces coding
# standards when this file is edited via vim.
#================================================
# vim:tabstop=4:shiftwidth=4:expandtab:smarttab
#================================================

###############################################################################
#   COMPONENT_NAME: CSPOC
#
# Name:
#       cdsh
#
# Description:
#       Performs remote execution of commands across the nodes of a cluster
#       and manages the stdout & stderr streams returned by those commands.
#
# Arguments:
#       prefix - temp file name prefix
#       node_list - comma separated list of nodes on which to run the underlying 
#		    AIX command
#       -q[1|2] - quiet flag
#                  suppresses output to stdout and/or stderr 
#       command - underlying AIX command to run
#       args - underlying AIX command arguments
#
# Return Values:
#       0 - successfully connected to all the nodes
#       2 - invalid arguments
#
###############################################################################
PROGNAME=${0##*/}
PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all)"
if [[ -n $_DEBUG ]] && (( $_DEBUG == 9 ))
then
    export PS4='${PROGNAME}[$LINENO]: '
    print -u2 'Version 1.16.1.3'
    set -x
fi

###############################################################################
#
# Subroutine Name:
#	run_command
#
# Description:
#	Runs the given command on the specified host.  Displays a message with
#	the return code if non-zero.
#
# Arguments:
#	Target host name
#
# Environment:
#	command - encoded command to run on remote node
#	CEXEC - command driver on remote node
#	emsg_cmd - command plus operands to display in error message
#
# Output:
#	command stdout and stderr
#	error message in case of error
#
function run_command {
    #
    :	Run the command on the specified host.  Print err msg if needed.
    #
    typeset host=$1
    /usr/es/sbin/cluster/utilities/cl_rsh -n $host ${CEXEC} $command
    typeset RC=$?
    if (($RC))
    then
	#
	:   Get the actual script name that we are to execute
	:   to correctly fill in error message for logs
	#
	#   Have the following cases for $command:
	#   1) eval <encoded string>
	#   2) command <encoded string>
	#   3) command -<flag> <encoded_string> -<flag> <encoded_string> ...
	#   4) command
	#
	print $command | read CMD ecmd		#   CMD is typically but not always 'eval'
	for word in $ecmd 
	do
	    if [[ $word == -* ]]
	    then
		demsg_cmd="$demsg_cmd $word"
	    else
		demsg_cmd="$demsg_cmd $(print $word | /usr/es/sbin/cluster/cspoc/cldecodearg)"
	    fi
	done
	if [[ $CMD == "eval" ]]
	then
	    print $demsg_cmd | read demsg_cmd demsg_cmd_opts
	else
	    demsg_cmd_opts=$demsg_cmd
	    demsg_cmd=$CMD			#   CMD is the command that will be run
	    CMD=""
	fi
	emsg_cmd="${CEXEC} $CMD $demsg_cmd $demsg_cmd_opts"
        print -u2 "cdsh: cl_rsh: (RC=$RC) $emsg_cmd"
    fi
}

###############################################################################
#
# Subroutine Name:
#	FixUp
#
# Description:
#	Processes stdout and stderr from a command run on a remote node.  
#	These always go to a log file, and may go on to stdout/stderr, or
#	be suppressed, based on caller input.
#
# Arguments:
#	Output type to be processed  - "stdout" or "stderr"
#
# Environment:
#	suppress - output to be suppressed - "stdout", "stderr", "both", or
#		   "none"
#	host - target node for command
#	OUT_FILE - caller provided log file name
#
# Output:
#	command stdout and stderr, log file contents
#
function FixUp {
    #
    :	Suppress output if selected, otherwise prepend the hostname
    #
    typeset OUTPUT_TYPE=$1

    if [[ $OUTPUT_TYPE == stdout ]]
    then
	#
        :   Process the stdout
	#
        if [[ $suppress == @($OUTPUT_TYPE|both) ]]
        then
	    #	
            :	Prepend hostname and send to logfile only
	    #
            cat | sed "s/^/${host}: /" >> $OUT_FILE
        else
	    #
            :	Prepend hostname and send to logfile and FD3 
	    :	which is parent FD1
	    #
            cat | sed -u "s/^/${host}: /" | tee -a $OUT_FILE >&3
        fi
    else
	#
        :   Process the stderr
	#
        if [[ $suppress == @($OUTPUT_TYPE|both) ]]
        then
	    #
            :	Prepend hostname and send to logfile only
	    #
            cat | sed "s/^/${host}: /" >> $ERR_FILE
        else
	    #
            :	Prepend hostname and send to logfile and STDERR
	    #
            cat | sed -u "s/^/${host}: /" | tee -a $ERR_FILE | grep -uv ": RETURN_CODE=" >&2
        fi
    fi
}

##############################################################################
#
#   Main MAIN main
#

#
:   Check usage
#
if (( $# < 3 )) 
then
    USAGE='USAGE: cdsh <prefix> <node[,node,...]> [-q[1|2]] <command> [args]\n'
    nls_msg -2 2 3 "$USAGE"
    return 2
fi

#
:   Local variables
#
typeset	CEXEC=/usr/es/sbin/cluster/cspoc/cexec

#
:   Get first two args: Temp file prefix and Node list
#
typeset	PREFIX=$1			\
	OUT_FILE=$PREFIX.out		\
	ERR_FILE=$PREFIX.err		\
	NODE_LIST=$2			\
# NOTE: THIS LINE ENDS CONTINUATION OF PREVIOUS LINE
shift 2				# Skip past prefix and node list

#
:   Ensure the error and output files exist
#
touch ${OUT_FILE}
touch ${ERR_FILE}

#
: Suppress display of stderr and stdout as required
# Notes: 
#  - The return code that cexec stuffed into stderr is stripped
#    from the output returned to the user.
#  - Output from cdsh is appended to the temp files so that there
#    is one temp file per try statement.
#  - The quiet options supress stdout, stderr, or both as required.
#
typeset suppress=""
case $1 in
  -q1)	:   suppress stdout
	suppress=stdout
	shift
	;;
  -q2)	:   suppress stderr
	suppress=stderr
	shift
	;;
  -q)	:   suppress both stdout and stderr
	suppress=both
	shift
	;;
   *)	:   do not supress stdout or stderr
        suppress=none
	;;
esac

#
:   Get the actual script name that we are to execute
#
command=$*		    		#   Complete, with operands

#
:   Open FD3 and redirect to STDOUT so that FixUp can send direct to STDOUT
#
exec 3>&1

#
:   Executing the command on the nodes ${NODE_LIST}
#
for host in $(IFS=, set -- ${NODE_LIST} ; print $* )
do
    #
    :	For each host, run and post-process the output
    #
    { run_command $host | FixUp stdout; } 2>&1 | FixUp stderr
done

exit 0
