#!/bin/ksh export try_out try_err cspoc_tmp_log export FPATH=/usr/es/sbin/cluster/cspoc cspoc_tmp_log=/var/hacmp/log/cel$$_tmplog log_cmd $cspoc_tmp_log $0 $* trap 'cexit $cspoc_tmp_log $?' EXIT function cel_f1 { cel_s1=/tmp/cel$$_s1 try_err=${cel_s1}.err try_out=${cel_s1}.out trap "log_output $cspoc_tmp_log ${cel_s1} eval $lsuser_cmd" EXIT IFS=,$IFS for _NODE in $_TARGET_NODES; do cdsh $cel_s1 $_NODE -q eval $lsuser_cmd cel_rc=$(get_rc ${cel_s1} $_NODE) case $cel_rc in 1000) nls_msg -l $cspoc_tmp_log 2 12 "${_CMD_NAME}: Unable to connect to node ${_NODE}" "${_CMD_NAME}" "${_NODE}" >& 2 if [[ -z "${_SPOC_FORCE}" ]] then exit 1 fi ;; *) if [ $cel_rc != 0 ]; then nls_msg -l $cspoc_tmp_log ${_MSET} 2 "${_CMD_NAME}: User ${D_UserName} does not exist on node ${_NODE}\n" ${_CMD_NAME} ${D_UserName} ${_NODE} >& 2 RETCODE=1 fi ;; esac done IFS=${IFS#,} return $cel_rc } function cel_f2 { cel_s2=/tmp/cel$$_s2 try_err=${cel_s2}.err try_out=${cel_s2}.out trap "log_output $cspoc_tmp_log ${cel_s2} eval $PW_remote_cmd" EXIT IFS=,$IFS for _NODE in $OTHER_NODES; do cdsh $cel_s2 $_NODE -q1 eval $PW_remote_cmd cel_rc=$(get_rc ${cel_s2} $_NODE) case $cel_rc in 1000) nls_msg -l $cspoc_tmp_log 2 12 "${_CMD_NAME}: Unable to connect to node ${_NODE}" "${_CMD_NAME}" "${_NODE}" >& 2 if [[ -z "${_SPOC_FORCE}" ]] then exit 1 fi ;; *) if [ $cel_rc != 0 ]; then return 1 fi ;; esac done IFS=${IFS#,} return $cel_rc } function cel_f3 { cel_s3=/tmp/cel$$_s3 try_err=${cel_s3}.err try_out=${cel_s3}.out trap "log_output $cspoc_tmp_log ${cel_s3} eval $chk_cmd" EXIT IFS=,$IFS for _NODE in $_TARGET_NODES; do cdsh $cel_s3 $_NODE -q eval $chk_cmd cel_rc=$(get_rc ${cel_s3} $_NODE) case $cel_rc in 1000) nls_msg -l $cspoc_tmp_log 2 12 "${_CMD_NAME}: Unable to connect to node ${_NODE}" "${_CMD_NAME}" "${_NODE}" >& 2 if [[ -z "${_SPOC_FORCE}" ]] then exit 1 fi ;; *) if [ $cel_rc != 0 ]; then print -u2 "No entry for /etc/security/passwd in HACMPfcmodtime found on node $_NODE" fi ;; esac done IFS=${IFS#,} return $cel_rc } function cel_f4 { cel_s4=/tmp/cel$$_s4 try_err=${cel_s4}.err try_out=${cel_s4}.out trap "log_output $cspoc_tmp_log ${cel_s4} eval $E_updt_modtime_cmd" EXIT IFS=,$IFS for _NODE in $updt_nodes; do cdsh $cel_s4 $_NODE -q1 eval $E_updt_modtime_cmd cel_rc=$(get_rc ${cel_s4} $_NODE) case $cel_rc in 1000) nls_msg -l $cspoc_tmp_log 2 12 "${_CMD_NAME}: Unable to connect to node ${_NODE}" "${_CMD_NAME}" "${_NODE}" >& 2 if [[ -z "${_SPOC_FORCE}" ]] then exit 1 fi ;; *) if [ $cel_rc != 0 ]; then return 1 fi ;; esac done IFS=${IFS#,} return $cel_rc } # ALTRAN_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # Copyright (C) Altran ACT S.A.S. 2017,2019,2021. All rights reserved. # # ALTRAN_PROLOG_END_TAG # # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r721 src/43haes/usr/sbin/cluster/cspoc/plans/cl_chpasswd.cel 1.15.1.3 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 2000,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 # @(#) 5dd6d3b 43haes/usr/sbin/cluster/cspoc/plans/cl_chpasswd.cel, 61aha_r726, 2205A_aha726, Nov 29 2021 07:19 PM ################################################################################## # # Name: # cl_chpasswd.cel # # Description: # The cl_chpasswd command changes user password for a user on all nodes # in an HACMP cluster. # # Usage: cl_chpasswd [-cspoc "[-d DebugLevel] [-f][ -g ResoureGroup # | -n NodeList ]\"] [-r] [-k] UserName [Password] # # Arguments: # The cl_chpasswd command arguments include the following: # -r Script was invoked by root from clpasswd. # -k KEEP: allow user to keep this new password # Unset the ADMCHG flag in /etc/security/passwd # UserName Name of user whose password will change # Password Optional password: # - If the password is not provided, # /usr/bin/password will prompt for it # - If the password is provided, # it will be passed to chpasswd # # The C-SPOC specific arguments are as follows: # -d 1..9 Debug level (not a public flag) # -f C-SPOC force flag # This flag forces execution of this command # even when one or more nodes is inaccessible # or the userid is not know on them all # -n NodeList List of nodes on which to execute command # Default is all cluster nodes # -g ResourceGroup Resource group whose node list will be used # as the list of nodes on which to execute # # Environment: # mode == "LDAP" Specify '-R LDAP' on the various password commands # # Restrictions: # The UserName whose password is being changed must be known on the local node # # Return Values: # 0 success # 1 failure # ################################################################################### # : Include the PATH and PROGNAME initialization stuff # # @(#)69 1.8 src/43haes/usr/sbin/cluster/cspoc/plans/cl_path.cel, hacmp.cspoc, 61haes_r720, 1539B_hacmp720 9/10/15 13:28:25 # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r720 src/43haes/usr/sbin/cluster/cspoc/plans/cl_path.cel 1.8 # # Licensed Materials - Property of IBM # # Restricted Materials of IBM # # COPYRIGHT International Business Machines Corp. 1999,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 ################################################################################ # COMPONENT_NAME: CSPOC # # Name: # cl_path.cel # # Description: # C-SPOC Path Initialization Routine. This routine is to be included # in all C-SPOC Execution Plans (e.g. '%include cl_path.cel'). # it sets up the PATH environment variable to prevent hardcoding of # path names in the CSPOC code. # # Arguments: # None. # # Return Values: # None. # # Environment Variables Defined: # # PUBLIC: # PROGNAME Represents the name of the program # HA_DIR Represents the directory the HA product is shipped under. # ################################################################################ PROGNAME=${0##*/} PATH="$(/usr/es/sbin/cluster/utilities/cl_get_path all)" # set the HA_DIR env variable to the HA directory HA_DIR="es" # Set up useful prompt for when 'set -x' is turned on through _DEBUG if [[ -n $_DEBUG ]] && (( $_DEBUG == 9 )) then PS4='${PROGNAME:-$_CMD}[$LINENO]: ' set -x fi [[ -n $_DEBUG ]] && print "$PROGNAME version $Source: 61haes_r711 43haes/usr/sbin/cluster/cspoc/plans/cl_chpasswd.cel 2/CHECKEDOUT$" # : Initialize variables needed to parse input # _CMD_NAME=${0##*/} _CSPOC_OPT_STR="d:f?[n:g:]" _OPT_STR="+1kr?" _USAGE="$(dspmsg -s 106 cspoc.cat 1 'Usage: cl_chpasswd [-cspoc \"-f [ -g ResourceGroup | -n NodeList ]\"] [-r] [-k] UserName [Password]\n')" _MSET=106 _USER_GROUP_MSET=39 RETCODE=0 LOG_FILE="/var/hacmp/log/clutils.log" # : Default exception handling for a UNABLE_TO_CONNECT error # # @(#)86 1.3 src/43haes/usr/sbin/cluster/cspoc/plans/cl_connect.cel, hacmp.cspoc, 61haes_r714 6/1/01 15:50:52 # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r714 src/43haes/usr/sbin/cluster/cspoc/plans/cl_connect.cel 1.3 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 1996,2001 # 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 # $Id: cl_connect.cel,v 1.1 1996/04/15 19:24:59 emy Exp $ # # COMPONENT_NAME: CSPOC # # FUNCTIONS: # # ORIGINS: # # # (C) COPYRIGHT International Business Machines Corp. 1996 # All Rights Reserved # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # ################################################################################ # # Name: # cl_init.cel # # Description: # Defines default exception handling for an UNABLE_TO_CONNECT error # in C-SPOC Execution Plans (e.g. '%include cl_connect.cel'). # The exception handling causes the command to print an error and # exits if dsh is unable to connect to a node, unless the force flag # is set. # # Arguments: # None. # # Return Values: # 1 If could not connect to a cluster node and force flag not set. # ################################################################################ # Default exception handling for a COULD_NOT_CONNECT error # : This script requires HA 5.4.1.0 or higher # _VER="5410" _VERSION="5.4.1.0" # : C-SPOC Initialization and Verification # # ALTRAN_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # Copyright (C) Altran ACT S.A.S. 2017,2018,2019,2021. All rights reserved. # # ALTRAN_PROLOG_END_TAG # # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r721 src/43haes/usr/sbin/cluster/cspoc/plans/cl_init.cel 1.16.7.9 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 1996,2016 # 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 # @(#) 7d4c34b 43haes/usr/sbin/cluster/cspoc/plans/cl_init.cel, 726, 2147A_aha726, Feb 05 2021 09:50 PM ################################################################################ # # COMPONENT_NAME: CSPOC # # Name: # cl_init.cel # # Description: # C-SPOC Initialization Routine. This routine is to be included # in all C-SPOC Execution Plans (e.g. '%include cl_init.cel'). # It defines the ksh functions required to implement C-SPOC commands. # # Arguments: # None. # # Return Values: # None. # # Environment Variables Defined: # # PUBLIC: # _OPT_STR Specifies the list of valid command flags. # Must be specified in the execution plan. # # _CSPOC_OPT_STR Specifies the list of valid CSPOC flags. # Must be specified in the execution plan. # # cspoc_tmp_log Full path of the cspoc log file # (/var/hacmp/log/cspoc.log). # # _CLUSTER_NODES A comma separated list of all nodes in the cluster. # # _NODE_LIST A comma separated list of nodes from the command # line (i.e. Those specified by -n or implied by -g). # # _TARGET_NODES A comma separated list that specify the target # nodes for a generated C-SPOC script. # # BADNODES A space-separated list that specifies the nodes # that are either not defined in the cluster or not # reachable for a generated C-SPOC script. # # _RES_GRP The resource group specified by -g on the # command line # # _SPOC_FORCE Set to "Y" when -f specified. Otherwise not set. # # _DEBUG Set to when -d specified. # Otherwise not set. # # _CMD_ARGS The AIX Command Options and arguments from the # C-SPOC command # # _NUM_CMD_ARGS The number of AIX Command Options and arguments # from the C-SPOC command # # _NON_FLG_ARGS The non-flag arguments from the C-SPOC command. # # _OF_NA A list of the optional command flags specified # that do NOT require an option argument. # # _MF_NA A list of the mandatory command flags specified # that do NOT require an option argument. # # _OF_WA A list of the optional command flags specified # that require an option argument. # # _MF_WA A list of the mandatory command flags specified # that require an option argument. # # _VALID_FLGS A list of valid command flags. # # _CSPOC_OPTS The CSPOC Options specified on the command line # following the '-cspoc' flag. # # _CSPOC_OF_NA A list of the optional CSPOC flags specified that # do NOT require an option argument. # # _CSPOC_MF_NA A list of the mandatory CSPOC flags specified that # do NOT require an option argument. # # _CSPOC_OF_WA A list of the optional CSPOC flags specified that # require an option argument. # # _CSPOC_MF_WA A list of the mandatory CSPOC flags specified that # require an option argument. # # _CSPOC_VALID_FLGS A list of valid CSPOC flags for this CSPOC command. # # CLUSTER_OVERRIDE Flag to Cluster Aware AIX Commands to signal that # base AIX commands should be allowed to operate. # Applies to 7.1.0 and later. # ################################################################################ ################################################################################ # # _get_node_list # # DESCRIPTION: # Generates two lists _CLUSTER_NODES is a list of all nodes in the cluster. # ################################################################################ function _get_node_list { if [[ -n $_DEBUG ]] then print "DEBUG: Entering _get_node_list version 1.16.7.9" if (( $_DEBUG >= 8 )); then typeset PROGNAME="_get_node_list" set -x fi fi unset _CLUSTER_NODES typeset NODE IP_ADDR # : GET A comma separated LIST OF ALL NODES IN THE CLUSTER # _CLUSTER_NODES=$(IFS=, set -- $(clodmget -q "object = COMMUNICATION_PATH" -f name -n HACMPnode) ; print "$*") if [[ -n $_DEBUG ]] then print "DEBUG: CLUSTER NODES [${_CLUSTER_NODES}]" print "DEBUG: Leaving _get_node_list" fi # : ENSURE THAT NODES FOUND FOR THE CLUSTER # if [[ -z ${_CLUSTER_NODES} ]]; then nls_msg -2 21 6 \ "${_CMD}: The cluster does not appear to be configured - no nodes are defined. \n Configure the cluster, nodes and networks then try this operation again.\n" $_CMD return 1 fi return 0 } # End of "_get_node_list()" ################################################################################ # # _get_target_nodes # # DESCRIPTION # Sets environment variable $_TARGET_NODES to the list of cluster # on which the C-SPOC command is to be executed. # # 1 - If a node list was specified $_TARGET_NODES is set to # the nodes listed. # # 2 - If a resource group was specified $_TARGET_NODES is set # to the list of nodes that are participating in that # resource group. # # 3 - If neither a node list or resource group has been specified # then $_TARGET_NODES is set to a list of all nodes in the cluster. # ################################################################################ function _get_target_nodes { if [[ -n $_DEBUG ]] then print "DEBUG: Entering _get_target_nodes version 1.16.7.9" if (( $_DEBUG >= 8 )) then typeset PROGNAME="_get_target_nodes" set -x fi fi typeset NODE="" integer GTN_RC=-1 # : If given a node list, or the nodes in a resource group, use those # if [[ -n $_NODE_LIST || -n $_RG_NODE_LIST ]] then _TARGET_NODES=$(IFS=, set -- $_NODE_LIST $_RG_NODE_LIST ; print "$*") GTN_RC=0 # : If no node list given, assume all cluster nodes, if we can find them # elif [[ -n $_CLUSTER_NODES ]] then _TARGET_NODES="$_CLUSTER_NODES" GTN_RC=0 # : Else cannot figure out where to run this # else nls_msg -2 -l ${cspoc_tmp_log} 4 6 \ "%s: Unable to determine target node list!\n" "$_CMD" GTN_RC=1 fi return $GTN_RC } # End of "_get_target_nodes()" ################################################################################ # # _get_rgnodes # # DESCRIPTION # Gets a list of nodes associated with the resource group specified. # ################################################################################ function _get_rgnodes { if [[ -n $_DEBUG ]] then print "DEBUG: Entering _get_rgnodes version 1.16.7.9" if (( $_DEBUG >= 8 )) then typeset PROGNAME="_get_rgnodes" set -x fi fi if [[ -z $1 ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9 \ "%s: _get_rgnodes: A resource group must be specified.\n" "$_CMD" return 1 fi _RG_NODE_LIST=$(clodmget -q "group = $1" -f nodes -n HACMPgroup) if [[ -z $_RG_NODE_LIST ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 50 \ "%s: Resource group %s not found.\n" "$_CMD" "$1" return 1 fi return 0 } # End of "_get_rgnodes()" ####################################################################### # # _getopts # # DESCRIPTION # Parses comand line options for C-SPOC commands. # ####################################################################### # # OPTION STRING # The _getopts() routine requires the execution plan to define the # environment variable $_OPT_STR which is refered to as the option # string. The option string is used to define valid and/or required # flags, the required number of non-flag arguments, and what flags # may or may not be specified together. # # Operator Description Example # -------- ------------------------------------------ --------- # () Groups mutually required flags (c!d:) # [] Groups mutually exclusive flags [f,b,] # # ? Optional flag (default) b? # ! Mandatory flag c! # # : Optional flag that requires an argument d: # ^ Mandatory flag that requires an argument e^ # # . Optional multi-byte flag # , Mandatory multi-byte flag f, # # +N Indicates that N non-flag arguments are. +2 # required. It must be at the beginning of # the option string. # # Notes: # 1 - A flag that can be specified with or without an argument # would be specified twice as follows: _OPT_STR="a?a:" # # 2 - A flag that requires an argument cannot also be the first # letter of a multi-byte flag. (i.e. -b arg -boot ) as there # is no way to differentiate between the two. # # Example: # The following option string would correspond to the usage below # In the usage '[]' indicates optional flags and '()' indicates # grouping. # # _OPT_STR="+2ab?(c!d:)e^[f,b,]g." # # Usage: # cmd [-a] [-b] -c [-d arg] -e arg ( -foo | -bar ) [-go] arg1 arg2 [arg3] # # ####################################################################### function _getopts { if [[ -n $_DEBUG ]] then print "DEBUG: Entering _getopts 1.16.7.9" if (( $_DEBUG >= 8 )) then typeset PROGNAME="_get_opts" set -x fi fi typeset CMD=${0##*/} # unset the following variables to avoid these variables being # influenced implicitly by external environment. Note that we will # not unset/touch _DEBUG since it is being checked even before hitting # this part of the code. i.e. depending upon the _DEBUG flag we set # set -x option initially itself. unset _NODE_LIST unset _RES_GRP unset _CSPOC_QUIET # LOCAL VARIABLES typeset _OPT_STR _CSPOC_OPT_STR OPT X Y typeset _VALID_FLGS _CSPOC_VALID_FLGS typeset _OF_NA _MF_NA _OF_WA _MF_WA typeset _CSPOC_OF_NA _CSPOC_MF_NA _CSPOC_OF_WA _CSPOC_MF_WA typeset _GOPT=no _NOPT=no # THE FIRST TWO ARGS MUST BE OPTION STRINGS _CSPOC_OPT_STR=$1 _OPT_STR=$2 shift 2 # CHECK CSPOC OPT STRING SPECIFIED IN THE EXECUTION PLAN # FOR OPTIONAL OR REQUIRED FLAGS [[ $_CSPOC_OPT_STR == *g^* ]] && _GOPT=req [[ $_CSPOC_OPT_STR == *g:* ]] && _GOPT=opt [[ $_CSPOC_OPT_STR == *n^* ]] && _NOPT=req [[ $_CSPOC_OPT_STR == *n:* ]] && _NOPT=opt # CHECK IF THE OPTION STRINGS SPECIFY A REQUIRED NUMBER OF NON-FLAG ARGS if [[ $_OPT_STR == +* ]] then X=${_OPT_STR#??} Y=${_OPT_STR%"$X"} _OPT_STR=$X _NUM_ARGS_REQ=${Y#?} fi # PARSE THE OPTION STRING ($_OPT_STR) INTO FIVE LISTS # ${_OF_NA} is a list of optional flags that DO NOT take an option arg. # ${_MF_NA} is a list of mandatory flags that DO NOT take an option arg. # ${_OF_WA} is a list of mandatory flags that DO take an option argument # ${_MF_WA} is a list of optional flags that DO take an option argument # ${_VALID_FLGS} is a list of all valid flags. # Note that both strings start and end with a space (to facilitate grepping) # and contain a list of space separated options each of which is preceded # by a minus sign. # THE FOLLOWING WHILE LOOP SIMPLY ORGANIZES THE VALID FLAGS INTO # FOUR LISTS THAT CORRESPOND TO THE FOUR FLAG TYPES LISTED ABOVE # AND A FIFTH LIST THAT INCLUDES ALL VALID FLAGS. X=${_OPT_STR} [[ $X == '-' ]] && X="" while [[ -n ${X} ]] do # GET THE NEXT LETTER OF THE OPTION STRING Y=${X#?} OPT=${X%"$Y"} X=${Y} # CHECK FOR AND PROCESS MUTALLY REQUIRED OR MUTUALLY EXCLUSIVE FLAGS case $OPT in '(') # STARTS A GROUP OF MUTUALLY REQUIRED FLAGS if [[ -n $MUTREQ ]] then print '$_CMD: _getopts: Invlid format for $_OPT_STR' print '$_CMD: _getopts: Unexpected character "("' return 1 fi MUTREQ=Y continue ;; ')') # ENDS A GROUP OF MUTUALLY REQUIRED FLAGS if [[ -z $MUTREQ ]] then print '$_CMD: _getopts: Invlid format for $_OPT_STR' print '$_CMD: _getopts: Unexpected character ")"' return 1 fi MUTREQ="" MUTREQ_FLAGS=$MUTREQ_FLAGS" " continue ;; '[') # STARTS A GROUP OF MUTUALLY EXCLUSIVE FLAGS if [[ -n $MUTEX ]] then print '$_CMD: _getopts: Invlid format for $_OPT_STR' print '$_CMD: _getopts: Unexpected character "["' return 1 fi MUTEX=Y continue ;; ']') # ENDS A GROUP OF MUTUALLY EXCLUSIVE FLAGS if [[ -z $MUTEX ]] then print '$_CMD: _getopts: Invlid format for $_OPT_STR' print '$_CMD: _getopts: Unexpected character "]"' return 1 fi MUTEX="" MUTEX_FLAGS=$MUTEX_FLAGS" " continue ;; esac # KEEP A LIST OF MUTUALLY EXCLUSIVE FLAGS if [[ -n $MUTEX && $MUTEX_FLAGS != *${OPT}* ]]; then MUTEX_FLAGS=${MUTEX_FLAGS}${OPT} fi # KEEP A LIST OF MUTUALLY REQUIRED FLAGS if [[ -n $MUTREQ && $MUTREQ_FLAGS != *${OPT}* ]]; then MUTREQ_FLAGS=${MUTREQ_FLAGS}${OPT} fi # KEEP A LIST OF ALL VALID FLAGS _VALID_FLGS="${_VALID_FLGS} -$OPT" # DETERMINE THE FLAG TYPE AS DESCRIBED ABOVE # ADD THE FLAG TO THE APPROPRIATE LIST AND # STRIP OFF THE FLAG TYPE IDENTIFIER FROM # THE OPTION STRING '${_OPT_STR}'. case $X in '.'*) # OPTIONAL MULTI-BYTE FLAG X=${X#.} _OF_MB="${_OF_MB} -$OPT" ;; ','*) # MANDATORY MULTI-BYTE FLAG X=${X#,} _MF_MB="${_MF_MB} -$OPT" ;; ':'*) # OPTIONAL FLAG THAT REQUIRES AN ARGUMENT X=${X#:} _OF_WA="${_OF_WA} -$OPT" ;; '^'*) # MANDATORY FLAG THAT REQUIRES AN ARGUMENT X=${X#^} _MF_WA="${_MF_WA} -$OPT" ;; '!'*) # MANDATORY FLAG X=${X#!} _MF_NA="${_MF_NA} -$OPT" ;; '?'*) # OPTIONAL FLAG X=${X#?} _OF_NA="${_OF_NA} -$OPT" ;; *) # OPTIONAL FLAG _OF_NA="${_OF_NA} -$OPT" ;; esac done # End of the option "while" loop # TACK A SPACE ONTO THE END OF EACH LIST TO MAKE OPTION GREPPING SIMPLE _VALID_FLGS=$_VALID_FLGS" " _OF_NA=$_OF_NA" " ; _OF_WA=$_OF_WA" " ; _OF_MB=$_OF_MB" " _MF_NA=$_MF_NA" " ; _MF_WA=$_MF_WA" " ; _MF_MB=$_MF_MB" " if [[ -n $_DEBUG ]] && (( $_DEBUG >= 3 )) then print "DEBUG(3): _OF_NA=$_OF_NA" print "DEBUG(3): _MF_NA=$_MF_NA" print "DEBUG(3): _OF_WA=$_OF_WA" print "DEBUG(3): _MF_WA=$_MF_WA" print "DEBUG(3): _OF_MB=$_OF_MB" print "DEBUG(3): _MF_MB=$_MF_MB" print "DEBUG(3): _VALID_FLGS=$_VALID_FLGS" fi # PARSE THE COMMAND LINE ARGS let _NUM_CMD_ARGS=0 while [[ -n $* ]] do THIS_FLAG=$1 THIS_ARG=${THIS_FLAG#??} THIS_FLAG=${THIS_FLAG%"$THIS_ARG"} if [[ -n $_DEBUG ]] then print "THIS_FLAG=\"$THIS_FLAG\"" print "THIS_ARG=\"$THIS_ARG\"" fi if [[ $1 == '-cspoc' ]] then # : Check for and process any CSPOC flags # _CSPOC_OPTS=$2 if [[ -z $_CSPOC_OPTS || $_CSPOC_OPTS == *([[:space:]]) ]] then SHIFT=1 else SHIFT=2 while getopts ':fd#n:?g:q' _CSPOC_OPTION $_CSPOC_OPTS do case $_CSPOC_OPTION in f ) : Force option export _SPOC_FORCE=Y ;; d ) : Debug level export _DEBUG=$OPTARG ;; n ) : Target node list export _NODE_LIST=$(print $OPTARG | sed -e"s/['\"]//g") ;; g ) : Target resource group export _RES_GRP=$(print $OPTARG | sed -e"s/['\"]//g") ;; q ) : Suppress output to stdout export _CSPOC_QUIET=YES ;; : ) : Missing operand - ignored ;; * ) : Invalid flag specified nls_msg -2 -l ${cspoc_tmp_log} 4 13 \ "%s: Invalid C-SPOC flag [%s] specified.\n" \ "$_CMD" "$_CSPOC_OPTION" print "$_USAGE" exit 2 ;; esac done fi # : Validate required and mutually exclusive CSPOC operands # if [[ $_GOPT == "no" && -n $_RES_GRP ]] then # : Is "-g" allowed # nls_msg -2 -l ${cspoc_tmp_log} 4 60 \ "%s: C-SPOC -g flag is not allowed for this command.\n" \ "$_CMD" print "$_USAGE" return 2 elif [[ $_NOPT == "no" && -n $_NODE_LIST ]] then # : Is "-n" allowed # nls_msg -2 -l ${cspoc_tmp_log} 4 61 \ "%s: C-SPOC -n flag is not allowed for this command.\n" \ "$_CMD" print "$_USAGE" return 2 elif [[ $_GOPT == "req" && $_NOPT == "req" ]] && \ [[ -z $_RES_GRP && -z $_NODE_LIST ]] then # : Check for "-g" or "-n" present when one : or the other is required # nls_msg -2 -l ${cspoc_tmp_log} 4 62 \ "%s: Either the '-g' or the '-n' C-SPOC flag must be specified.\n" "$_CMD" print "$_USAGE" return 2 elif [[ -n $_RES_GRP && -n $_NODE_LIST ]] then # : Check that both "-g" and "-n" are not specified together # nls_msg -2 -l ${cspoc_tmp_log} 4 63 \ "%s: C-SPOC -g and -n flags are mutually exclusive.\n" \ "$_CMD" print "$_USAGE" return 2 elif [[ $_NOPT != "req" && $_GOPT == "req" && -z $_RES_GRP ]] then # : Is only "-g" allowed # nls_msg -2 -l ${cspoc_tmp_log} 4 64 \ "%s: C-SPOC -g flag is required.\n" "$_CMD" print "$_USAGE" return 2 elif [[ $_GOPT != "req" && $_NOPT == "req" && -z $_NODE_LIST ]] then # : Is only "-n" required # nls_msg -2 -l ${cspoc_tmp_log} 4 65 \ "%s: C-SPOC -n flag is required.\n" "$_CMD" print "$_USAGE" return 2 fi shift $SHIFT elif [[ "$THIS_FLAG" != -* ]] then # AIX COMMAND ARGUMENT THAT IS NOT AN OPTION FLAG # NEED TO ACCOMODATE OPTIONS THAT MAY OR MAY NOT HAVE AN ARGUMENT. # IF OPT_ARG DOESN'T START WITH A '-' ITS AN ARGUMENT OTHERWISE # CONSIDER IT TO BE THE NEXT OPTION let _NUM_CMD_ARGS=$_NUM_CMD_ARGS+$# TMP_FLAG="" while (( $# > 0 )) do case "$1" in -*) TMP_FLAG=$(echo $1 | cut -c1-2) _CMD_ARGS=${_CMD_ARGS:+"${_CMD_ARGS} "}"$TMP_FLAG" TMP_ARG1=$(echo $1 | cut -c3-) if [[ -n $TMP_ARG1 ]] then TMP_ARG1="$(print -- $TMP_ARG1 |\ clencodearg $_ENCODE_ARGS)" _CMD_ARGS=${_CMD_ARGS:+"${_CMD_ARGS} "}"$TMP_ARG1" TMP_FLAG="" fi ;; *) TMP_ARG2="$(print -- $1 | clencodearg $_ENCODE_ARGS)" _CMD_ARGS=${_CMD_ARGS:+"${_CMD_ARGS} "}${TMP_ARG2} if [[ -z $TMP_FLAG ]] then _NON_FLG_ARGS=${_NON_FLG_ARGS:+"${_NON_FLG_ARGS} "}"${TMP_ARG2}" fi TMP_FLAG="" esac shift done break else # COME INTO HERE WITH $THIS_FLAG and $THIS_ARG SET ARG_CHECK=Y ARG_NEXT="" while [[ -n $ARG_CHECK ]] do # NOW CHECK IF WE STILL HAVE MORE FLAGS TO PROCESS [[ -z $THIS_ARG ]] && ARG_CHECK="" if print -- "$_OF_MB $_MF_MB" | grep -- "$THIS_FLAG" > /dev/null then # THIS IS A MULTI-BYTE FLAG if [[ -z $THIS_ARG ]] then ( print -- "$_OF_NA $_MF_NA" | grep -- "$THIS_FLAG" > /dev/null ) || \ { # THIS FLAG REQUIRES AN ARGUMENT nls_msg -2 -l ${cspoc_tmp_log} 4 19 \ "%s: Invalid option [%s].\n" "$_CMD" "$1" print "$_USAGE" exit 2 } fi # VALID AIX COMMAND MULTI-BYTE OPTION (WITHOUT AN ARGUMENT) _CMD_ARGS=${_CMD_ARGS:+"${_CMD_ARGS} "}"$THIS_FLAG$THIS_ARG" shift ARG_CHECK="" # Disable further processing of $THIS_ARG as flags elif print -- "$_OF_WA $_MF_WA" | grep -- "$THIS_FLAG" > /dev/null then # THIS IS A FLAG THAT REQUIRES AN ARGUMWENT # HANDLE OPTIONAL SPACE BETWEEN FLAG AND ITS ARG if [[ -z $THIS_ARG && -z $ARG_NEXT ]] then THIS_ARG=$2 # THERE WAS A SPACE SHIFT=2 else SHIFT=1 # THERE WAS NO SPACE fi # NOW VALIDATE THAT WE HAVE AN ARG AND THAT IT IS VALID if [[ -z $THIS_ARG || $THIS_ARG == -* ]] then # IF THERE IS NO ARG THEN CHECK IF FLAG MAY BE SPECFIED WITHOUT ONE print -- "$_OF_NA $_MF_NA" | grep -q -- "$THIS_FLAG" ||\ { # THIS FLAG REQUIRES AN ARGUMENT nls_msg -2 -l ${cspoc_tmp_log} 4 19 \ "%s: Option [%s] requires an argument.\n" "$_CMD" "$1" print "$_USAGE" exit 2 } fi # VALID AIX COMMAND OPTION WITH AN ARGUMENT _CMD_ARGS=${_CMD_ARGS:+"${_CMD_ARGS} "}"$THIS_FLAG $(print -- $THIS_ARG | clencodearg $_ENCODE_ARGS)" shift $SHIFT # Disable further processing of $THIS_ARG as flags ARG_CHECK="" elif print -- "$_OF_NA $_MF_NA" | grep -q -- "$THIS_FLAG" then # THIS IS A FLAG THAT DOES NOT TAKE AN ARGUMENT _CMD_ARGS=${_CMD_ARGS:+"${_CMD_ARGS} "}"$THIS_FLAG" # IF THIS FLAG WAS OBTAINED FROM $THIS_FLAG THEN WE WANT TO # SHIFT. IF IT WAS OBTAINED FROM $THIS_ARG THEN WE DONT [[ -z $ARG_CHECK ]] && shift # THIS FLAG DOES NOT TAKE AN OPTION ARGUMENT SO ASSUME # THAT "$THIS_ARG" SPECIFIES MORE FLAGS TO PROCESS. if [[ -n $THIS_ARG ]] then # GET THE NEXT FLAG, ADJUST $THIS_ARG, # AND KEEP PROCESSING. X=${THIS_ARG#?} THIS_FLAG="-${THIS_ARG%$X}" THIS_ARG=$X ARG_NEXT=Y fi else nls_msg -2 -l ${cspoc_tmp_log} 4 26 \ "%s: Invalid option [%s].\n" "$_CMD" "$1" print "$_USAGE" exit 2 fi done fi done ## # PERFORM CHECKING OF THE AIX COMMAND FLAGS ## # CHECK FOR REQUIRED NUMBER OF NON-FLAG ARGUMENTS if (( ${_NUM_CMD_ARGS:-0} < ${_NUM_ARGS_REQ:-0} )) then nls_msg -2 -l ${cspoc_tmp_log} 4 27 \ "%s: Missing command line arguments.\n" "$_CMD" print "$_USAGE" return 2 fi # THIS IS WHERE WE CHECK FOR MANDATORY FLAGS, MUTUALLY EXCLUSIVE FLAGS, # AND MUTUALLY REQUIRED FLAGS # CHECK FOR MUTUALLY REQUIRED FLAGS # FOR EACH GROUP OF FLAGS SPECIFIED IN $MUTREQ_FLAGS WE WILL COUNT HOW # MANY WE NEED AND HOW MANY ARE GIVEN ON CMD LINE. IF THESE VALUES ARE # NOT EQUAL PRINT AN ERROR AND RETURN NON-ZERO typeset -i CNT=0 N=0 for GROUP in $MUTREQ_FLAGS do # GET A COUNT OF HOW MANY FLAGS IN THIS GROUP print -n $GROUP | wc -c | read N integer CNT=0 F="" while [[ -n $GROUP ]] do # GET THE NEXT FLAG IN THE GROUP A=${GROUP#?} B=${GROUP%"$A"} GROUP=$A # IF THIS FLAG IS USED INCREMENT THE COUNTER if [[ "$(print -- $_CMD_ARGS | grep -- '-'${B})"' ' != ' ' ]] then (( CNT = CNT + 1 )) fi F=${F:+"$F, "}"-"$B done # VERIFY THAT THE COUNTER EQUALS THE TOTAL NUMBER OF FLAGS IN THE GROUP if (( $CNT != $N )) then print "$_CMD: One or more flags [$F] were not specified." print "$_CMD: Specifying any one of these flags requires the others." return 2 fi done # CHECK FOR MUTUALLY EXCLUSIVE FLAGS # FOR EACH GROUP OF FLAGS SPECIFIED IN $MUTEX_FLAGS WE WILL COUNT HOW # MANY ARE GIVEN ON CMD LINE. IF MORE THAN ONE IS GIVEN THEN PRINT # AN ERROR AND RETURN NON-ZERO for GROUP in $MUTEX_FLAGS do # GET A COUNT OF HOW MANY FLAGS IN THIS GROUP integer CNT=0 F="" while [[ -n $GROUP ]] do # GET THE NEXT FLAG IN THE GROUP A=${GROUP#?} B=${GROUP%"$A"} GROUP=$A # IF THIS FLAG IS USED INCREMENT THE COUNTER if [[ -n "$(print -- $_CMD_ARGS | grep -- '-'${B})" ]] then (( CNT = CNT + 1 )) fi F=${F:+"$F, "}"-"$B done # VERIFY THAT THE COUNTER EQUALS THE TOTAL NUMBER OF FLAGS IN THE GROUP if (( $CNT > 1 )) then print "$_CMD: The flags [$F] are mutually exclusive." print "$_CMD: Only one of these flags may be specified." return 2 fi done # CHECK FOR ALL MANDATORY FLAGS for X in $_MF_NA $_MF_WA do # CHECK THAT MANDATORY FLAG IS ON COMMAND LINE if [[ -z "$(print -- $_CMD_ARGS | grep -- ${X})" ]] then # THE FLAG WAS NOT SPECIFIED SO WE MUST FIRST CHECK IF ANOTHER # FLAG WAS SPECIFIED THAT IS MUTUALLY EXCLUSIVE WITH THIS ONE. for GROUP in $MUTEX_FLAGS do OK="" while [[ -n $GROUP ]] do Y=${GROUP#?} Z=${GROUP%"$Y"} GROUP=$Y print -- " $_CMD_ARGS " |\ grep -- "-${Z} " > /dev/null && OK=Y done [[ -n $OK ]] && break done # "$OK" IS NULL IF NO FLAG IN THIS MUTEX GROUP WAS GIVEN if [[ -z $OK ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 29 \ "%s: Mandatory option [%s] not specified.\n" "$_CMD" "$X" print "$_USAGE" return 2 fi fi done if [[ -n $_DEBUG ]] && (( $_DEBUG >= 3 )) then print -- "DEBUG(3): _CMD_ARGS=$_CMD_ARGS" fi return 0 } # End of "_getopts()" ################################################################################ # # DESCRIPTION: # Updates the C-SPOC logfile # ################################################################################ function cexit { if [[ -n $_DEBUG ]] then print "DEBUG: Entering cexit version 1.16.7.9" if (( $_DEBUG >= 8 )) then typeset PROGNAME=cexit set -x fi fi typeset USAGE="USAGE: cexit " # CHECK USAGE (( $# != 2 )) && print "$_CMD: $USAGE" typeset TEMP_LOG=$1 typeset RC=$2 # : Read the HACMPlogs ODM for the pathname of the cspoc.log log file : If the ODM is empty or corrupted, use /var/hacmp/log/cspoc.log # DESTDIR=$(clodmget -q "name = cspoc.log" -f value -n HACMPlogs) if [[ -n $DESTDIR ]] then CSPOC_LOG="$DESTDIR/cspoc.log" else dspmsg scripts.cat 463 "The cluster log entry for %s could not be found in the HACMPlogs ODM.\n" "cspoc.log" dspmsg scripts.cat 464 "Defaulting to log directory %s for log file %s.\n" "/var/hacmp/log" "cspoc.log" CSPOC_LOG="/var/hacmp/log/cspoc.log" fi # : CHECK ARGS # if [[ ! -f ${TEMP_LOG} ]] then nls_msg -2 -l ${CSPOC_LOG} 4 39 \ "%s: Unable to open file: %s\n" "${TEMP_LOG}" "$_CMD" return 1 fi # : If the log file does not exist, create it. # if [[ ! -f ${CSPOC_LOG} ]]; then touch ${CSPOC_LOG} fi # : Keep the information in the log file if we have write permission # if [[ -w $CSPOC_LOG ]] then cat ${TEMP_LOG} >> $CSPOC_LOG fi if (( $RC == 0 )) && ( [[ -z $_DEBUG ]] || (( $_DEBUG <= 8 )) ) then rm -f ${TEMP_LOG%_*}* rm -f /tmp/cel$$_s*.err rm -f /tmp/cel$$_s*.out rm -f /tmp/cel$$.cache fi } # End of "cexit()" ################################################################################ # # _cspoc_verify - Performs verification of a number of CSPOC requirments. # Certain requirements, if not met, produce a hard error # and the routine produces an immediate exit of the script. # Other requirements, if not met, produce soft errors that # result in the routine returning a value of '1'. The # calling script will then exit unless the CSPOC force flag # has been set. # ################################################################################ function _cspoc_verify { if [[ -n $_DEBUG ]] then print "DEBUG: Entering _cspoc_verify version 1.16.7.9 + 20527,842,758" if (( $_DEBUG >= 8 )) then typeset PROGNAME="_cspoc_verify" set -x fi fi typeset NODE typeset bad_targets # space separated list of unreachable nodes typeset CAA_down_nodes # target hosts CAA says are down typeset CAA_node_name # CAA host node name integer _RETCODE=0 # Assume OK until proven otherwise typeset BADNODES # Space separated list of invalid nodes typeset down_ha_nodes # target HA nodes CAA says are down typeset good_targets # target HA nodes that should work typeset bad_level_nodes # target HA nodes below minimum release level if [[ $_CSPOC_CALLED_FROM_SMIT != 'true' ]] then # : If not called from SMIT, which will surely set things : up correctly, check to make sure target nodes are valid. # for NODE in $(IFS=, set -- $_TARGET_NODES ; print $*) do # : Collect a list of given nodes that do not : show up in the local cluster definition. # if [[ $_CLUSTER_NODES != @(?(*,)$NODE?(,*)) ]] then BADNODES=${BADNODES:+$BADNODES" "}$NODE nls_msg -2 -l ${cspoc_tmp_log} 4 44 \ "%s: The node [%s] is not a part of this cluster.\n" "$_CMD" "$NODE" fi done if [[ -n $BADNODES ]] then # : Remove any invalid node names from the node list # save_targets="" for ha_node in $(IFS=, set -- $_TARGET_NODES ; print $*) do if [[ $BADNODES != @(?(* )${ha_node}?( *)) ]] then save_targets=${save_targets:+"${save_targets},"}${ha_node} fi done _TARGET_NODES=$save_targets if [[ -z $_TARGET_NODES ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will not be run because all of the target nodes, %s, are not part of this cluster\n" "$_CMD" $LINENO "$BADNODES" exit 1 # No valid nodes found else _RETCODE=1 # Continue if 'forced' specified fi fi fi cluster_version=$(clodmget -f cluster_version -n HACMPcluster) if [[ -x /usr/lib/cluster/incluster ]] && /usr/lib/cluster/incluster || \ (( $cluster_version >= 15 )) then # : If at a level where CAA is in place, check to see if : CAA can provide information on the state of nodes. # LC_ALL=C lscluster -m 2>/dev/null | \ egrep 'Node name:|State of node:' | \ cut -f2 -d: | \ paste -d' ' - - | \ while read CAA_node_name state do if [[ -n $CAA_node_name ]] then if [[ $state != 'UP' && \ $state != @(?(* )NODE_LOCAL?( *)) && \ $state != @(?(* )REACHABLE THROUGH REPOS DISK ONLY?( *)) && \ $state != 'DOWN STOPPED' ]] then # # The purpose of this check is to avoid long timeouts # trying to talk to a node known to be dead. # - The local node is always reachable # - A stopped node may be reachable; halevel checks below # - A node reachable only through the repository disk # may be reachable: just because CAA declares the # network to be down doesn't mean clcomd can't get # through; hlevel checks below # : Node $CAA_node_name is 'DOWN' # CAA_down_nodes=${CAA_down_nodes:+"${CAA_down_nodes} "}${CAA_node_name} # : Find the PowerHA node name corresponding to the : $CAA_node_name - the name must be a label on an : interface on some node. # host_ip=$(LC_ALL=C host $CAA_node_name | cut -f3 -d' ') host_ip=${host_ip%,} if [[ -n $host_ip && $host_ip == @(+([0-9.])|+([0-9:])) ]] then down_ha_node=$(clodmget -q "identifier = ${host_ip}" -f nodename -n HACMPadapter) if [[ -n $down_ha_node ]] then down_ha_nodes=${down_ha_nodes:+"$down_ha_nodes "}${down_ha_node} nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The CAA lscluster command indicates that node %s[%s] is \"%s\" and not active.\n" "$_CMD" $LINENO $down_ha_node $CAA_node_name "$state" fi fi fi fi done fi if [[ -n $down_ha_nodes ]] then # : CAA says that nodes $down_ha_nodes are not active : Construct a list of the remaining nodes, to use to : check to see if clcomd is running. # for ha_node in $(IFS=, set -- $_TARGET_NODES ; echo $* ) do if [[ $down_ha_nodes != @(?(* )${ha_node}?( *)) ]] then good_targets=${good_targets:+"${good_targets} "}${ha_node} fi done else # : CAA gives no reason to suspect nodes are not reachable # good_targets=$(IFS=, set -- $_TARGET_NODES ; echo $* ) fi # : CAA has not ruled out talking to node $good_targets # if [[ -n $_SPOC_FORCE ]] && /usr/lib/cluster/incluster then # : It is possible that the target node list contains names : that do not correspond to CAA host names after the CAA : cluster is created. # Before the CAA cluster is created, all target nodes are # naturally not in a CAA cluster. Ordinarily, this can be # left to clhaver to find, though it does not distinguish # between nodes it cannot connect to, and nodes that are # that are not in the CAA cluster. If the force flag was # specified, and we are already in a CAA cluster, : Silently elide names in the target list that do not : correspond to CAA host names. # save_targets=$good_targets good_targets="" for given_node in $save_targets do if cl_query_hn_id -q -i $given_node >/dev/null 2>&1 then good_targets=${good_targets:+"${good_targets} "}${given_node} else print "$(date) ${_CMD}._cspoc_verify[$LINENO]: Given target \"$given_node\" cannot be converted to a CAA host name. It will be skipped." >> $clutilslog fi done fi if [[ -n $good_targets ]] then # : CAA thinks that nodes \"$good_targets\" : are active. See if clcomd can talk to them, : and what level of PowerHA is present. # clhaver -c $_VER $good_targets | \ while IFS=: read ha_node caa_host VRMF do if [[ -z $caa_host ]] then # : Add $ha_node, which clhaver cannot communicate to : through clcomd, to the list of nodes not to try : to run the command on. # down_ha_nodes=${down_ha_nodes:+"${down_ha_nodes} "}${ha_node} elif (( $VRMF < $_VER )) then # : Add $ha_node to the list of nodes below the minimum : HA release level. # bad_level_nodes=${bad_level_nodes:+"${bad_level_nodes} "}${ha_node} fi done if [[ -n $bad_level_nodes ]] then # : Nodes \"$bad_level_nodes\" report that they are running a : version of PowerHA below the required level $_VERSION # if [[ -z $_SPOC_FORCE ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will not be run because the following nodes are below the required level %s: %s\n" "$_CMD" $LINENO $_VERSION "$bad_level_nodes" elif [[ $bad_level_nodes == $good_targets ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will not be run because all nodes are below the required level %s: %s\n" "$_CMD" $LINENO $_VERSION "$bad_level_nodes" else # : If force was specified, command processing continues : but skips nodes \"$bad_level_nodes\" # nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will be run, but not on the following nodes, which are below the required level %s: %s\n" "$_CMD" $LINENO $_VERSION "$bad_level_nodes" fi down_ha_nodes=${down_ha_nodes:+"${down_ha_nodes} "}${bad_level_nodes} _RETCODE=1 fi fi if [[ -n $down_ha_nodes ]] then # : The nodes in \$down_ha_nodes, \"$down_ha_nodes\", are not acceptable : targets for this command, either because CAA says they are down, : or clcomd cannot talk to them, or they are running too far a back : level of PowerHA. Remove them from the list of C-SPOC target nodes. # save_targets="" for ha_node in $good_targets do if [[ $down_ha_nodes != @(?(* )${ha_node}?( *)) ]] then save_targets=${save_targets:+"${save_targets} "}${ha_node} fi done good_targets=$save_targets bad_targets=$(IFS=, set -- $down_ha_nodes ; print "$*" ) if [[ -z $good_targets ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will not be run because all of the target nodes, %s, are not reachable\n" "$_CMD" $LINENO "$bad_targets" exit 1 elif [[ -n $bad_targets ]] then if [[ -z $_SPOC_FORCE ]] then if [[ $bad_targets == @(*,*) ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will not be run because the target nodes, %s, are not reachable\n" "$_CMD" $LINENO "$bad_targets" else nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will not be run because the target node, %s, is not reachable\n" "$_CMD" $LINENO "$bad_targets" fi _RETCODE=1 else if [[ $bad_targets == @(*,*) ]] then nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will be run, but not on the unreachable nodes %s\n" "$_CMD" $LINENO "$bad_targets" else nls_msg -2 -l ${cspoc_tmp_log} 4 9999 \ "%s[%d]: The command will be run, but not on the unreachable node %s\n" "$_CMD" $LINENO "$bad_targets" fi fi fi fi _TARGET_NODES=$(IFS=, set -- $good_targets ; print "$*" ) # : \$_TARGET_NODES, \"$_TARGET_NODES\", is a list of nodes that are : up, contactable by clcomd, and running a reasonably up to date : level of PowerHA. # return $_RETCODE } # End of "_cspoc_verify()" ################################################################################ # # Start of main, Main, MAIN # ################################################################################ if [[ -n $_DEBUG ]] then print "\n[C-SPOC Initialization Started version 1.16.7.9" fi _VER=${_VER:-"6100"} _VERSION=${_VERSION:-"6.1.0.0"} export CLUSTER_OVERRIDE="yes" # Allow CAAC commands to run... 710 _CMD=${0##*/} integer TRY_RC=0 # : since root is needed to determine node lists and what not - clgetaddr : we may as well disable everything right here right now. By putting : in an explicit check we can provide a more intuitive message rather : than something about not being able to execute some command later on. # if [[ $(whoami) != "root" ]] && ! ckauth PowerHASM.admin then nls_msg -2 -l ${cspoc_tmp_log} 4 52 \ "%s: All C-SPOC commands require the user to either be root, or have PowerHASM.admin authorization\n" "$_CMD" exit 2 fi # : Set a default value, unless this script is called from SMIT, in which : case _CSPOC_MODE will already be defined. By default, this should determine : what the request mode type. # export _CSPOC_MODE=${_CSPOC_MODE:-"both"} # : By default, assume that we are being called from the command line # export _CSPOC_CALLED_FROM_SMIT=${_CSPOC_CALLED_FROM_SMIT:-"false"} # : Make sure that the _CMD_ARGS variable is visible everywhere # export _CMD_ARGS="" [[ -n $_DEBUG ]] && print "\n[Parsing Command Line Options ... ]" # : Tell clencodearg to skip the special escape processing for '=' # if [[ $SKIP_EQ_ESC == true ]] then export _ENCODE_ARGS="-e" else export _ENCODE_ARGS="" fi _CSPOC_OPT_STR=${_CSPOC_OPT_STR:--} _OPT_STR=${_OPT_STR:--} _getopts "$_CSPOC_OPT_STR" "$_OPT_STR" "$@" || exit 1 if [[ -n $_DEBUG ]] then print "_CMD_ARGS=${_CMD_ARGS}" print "_NUM_CMD_ARGS=${_NUM_CMD_ARGS}" print "_NON_FLG_ARGS=${_NON_FLG_ARGS}" print "\n[Getting Cluster Node List ... ]" fi # : Determine the nodes in the cluster, and the nodes to which this operation : aplies. # export ODMDIR=/etc/objrepos _get_node_list || exit 1 _get_target_nodes || exit 1 if [[ -n $_DEBUG ]] then print "_CLUSTER_NODES=${_CLUSTER_NODES}" print "\n[Verifying C-SPOC Requirements ... ]" fi if [[ -z $clutilslog ]] then clutilslog=$(clodmget -q 'name = clutils.log' -f value -n HACMPlogs)"/clutils.log" fi # : If not all nodes are reachable, stop now, unless the "force" flag was : specified, implying continue despite unreachable nodes # _cspoc_verify || { [[ -z $_SPOC_FORCE ]] && exit 1 } if [[ -n $_DEBUG ]] then print "\n[C-SPOC Initialization Completed.]" print "DEBUG: Entering ${0##*/}" (( $_DEBUG >= 8 )) && set -x fi # : User and optional password allowed # if [[ ${_NUM_CMD_ARGS} > 2 ]] then print "$_USAGE" return 2 fi # : Extract options # KEEP=0 ROOT=0 while getopts ':kr' option $_CMD_ARGS do case $option in k ) : Turn off ADMCHG flag ; KEEP=1 ;; r ) : Run by root ; ROOT=1 ;; * ) : Invalid input print $_USAGE return 2 ;; esac done # : Extract UserName and possible PassWord # print $_NON_FLG_ARGS | read UserName PassWord # : Construct a decoded username for the error messages. # D_UserName=$(print $UserName | cldecodearg) RETCODE=0 if [[ -z ${_SPOC_FORCE} ]] then # : If "force" was not specified, check if user : user exists across all cluster nodes # if [[ $mode == "LDAP" ]]; then lsuser_cmd=$(print -- "lsuser -R LDAP -c -a $D_UserName | grep -v ^#" | clencodearg -e) else lsuser_cmd=$(print -- "lsuser -R files -c -a $D_UserName | grep -v ^#" | clencodearg -e) fi cel_f1 (( $RETCODE != 0 )) && return 1 fi LOCAL_NODE=$(get_local_nodename) # : Process depending on whether interactive or passed password # if [[ -z $PassWord ]] then # # The careful reader will note the implicit assumption that the local node is # part of _TARGET_NODES. This appears to be unavoidable for interactive # password change. # if [[ $mode == "LDAP" ]] then # : For LDAP defined users, it is sufficient to run the command on : the local node. # if (( $ROOT == 1 )) then passwd -R LDAP $D_UserName else /usr/bin/su - $D_UserName -c "passwd -R LDAP $D_UserName" fi else # non-LDAP # : Run the passwd User command on the local node to create a new local password # config_file=/usr/es/sbin/cluster/etc/clpasswd/config config_init=/usr/es/sbin/cluster/etc/clpasswd/config.init bkup_passwd_loc='/usr/es/sbin/cluster/etc/clpasswd/usr_bin_passwd.orig' ODMDIR=/usr/lib/objrepos clodmget -q "loc0 = /usr/bin/passwd" -n -f size,checksum inventory | IFS=: read inventory_size inventory_checksum if ( grep -q '^/usr/bin/passwd:.*:Unlinked' $config_file || [[ ! -s $config_file ]] ) && [[ "l" != $(ls -l /usr/bin/passwd | cut -c1) ]] then # : Verification checking for unlinked # if [[ ! -x /usr/bin/passwd && ! -x /bin/passwd ]] then # : The config file says passwd is unlinked, but the original binaries : are not in place. Check for a saved backup. # if [[ -x $bkup_passwd_loc && "l" != $(ls -l $bkup_passwd_loc | cut -c1) ]] then # : Make sure the saved file matches the inventory data base # sum $bkup_passwd_loc | read bkup_checksum rest ls -l $bkup_passwd_loc | read skip skip skip skip bkup_size rest if (( $bkup_checksum == $inventory_checksum && $bkup_size == $inventory_size )) then # : Have a saved executable file that looks good. Try to restore it. # (( ROOT == 1 )) && chmod +s $bkup_passwd_loc cp -p $bkup_passwd_loc /usr/bin/passwd 2>> $LOG_FILE rc=$? [[ ! -x /bin/passwd ]] && cp -p $bkup_passwd_loc /bin/passwd 2>> $LOG_FILE (( rc == 0 )) && rc=$? else rc=99 # Backup file does not match inventory data base fi else rc=99 # No executable passwd command or backup fi if (( $rc != 0 )) then nls_msg -l $cspoc_tmp_log ${_MSET} 14 "${_CMD_NAME}: ERROR! The AIX \"passwd\" command is either missing or not executable and could not be restored from the intended backup copy ${bkup_passwd_loc}.\n" ${_CMD_NAME} ${bkup_passwd_loc} >& 2 return $rc else nls_msg -l $cspoc_tmp_log ${_MSET} 9999 "${_CMD_NAME}: AIX passwd command restored from back up.\n" ${_CMD_NAME} >& 2 fi fi # : The AIX passwd command is not linked to the PowerHA clpasswd command. : And the AIX passwd command looks valid. : So, should be able to run it from its normal location. # if (( ROOT == 1 )) then Command="passwd $D_UserName" passwd $D_UserName else Command="/usr/bin/su - $D_UserName -c \"passwd $D_UserName\"" /usr/bin/su - $D_UserName -c "passwd $D_UserName" fi elif grep -q '^/usr/bin/passwd:.*:Linked' $config_file && [[ "l" == $(ls -l /usr/bin/passwd | cut -c1) ]] then # : The AIX passwd command is linked to clpasswd. Look for the original : passwd command. # if ! grep -q "^${bkup_passwd_loc}"':.*:Unlinked' $config_file then # : The $config_file does not contain the expected pointer to the : real AIX passwd command. This may be due to corruption of : that file. Note the condition. # nls_msg -l $cspoc_tmp_log ${_MSET} 9999 "${_CMD_NAME}: Backup copy of AIX passwd command not found in ${config_file}.\n" ${_CMD_NAME} ${config_file} >& 2 cat $config_init | sed "s|^|$config_init |" >> $LOG_FILE cat $config_file | sed "s|^|$config_file |" >> $LOG_FILE fi # : Despite the corruption of $config_file, see if the backup : does actually exist. # if [[ -x ${bkup_passwd_loc} && "l" != $(ls -l $bkup_passwd_loc | cut -c1) ]] then # : There is an executable file in the backup location, and it is not : itself a link. # sum $bkup_passwd_loc | read bkup_checksum rest ls -l $bkup_passwd_loc | read skip skip skip skip bkup_size rest if (( $inventory_size == $bkup_size && $inventory_checksum == $bkup_checksum )) then # : There is a saved backup copy of the real passwd comamnd. : Use that. # if (( ROOT == 1 )) then # : Store the command to be executed in the Command for logging : Let the result of execution be captured in PASSWD_RETCODE, to correctly return the result. # Command="$bkup_passwd_loc $D_UserName" $bkup_passwd_loc $D_UserName else Command="/usr/bin/su $D_UserName -c \"$bkup_passwd_loc $D_UserName\"" /usr/bin/su $D_UserName -c "$bkup_passwd_loc $D_UserName" fi else # : Size and checksum do not match # nls_msg -l $cspoc_tmp_log ${_MSET} 999999 "${_CMD_NAME}: ERROR! ${bkup_passwd_loc} does not appear to contain a copy of the AIX passwd command.\n" ${_CMD_NAME} ${bkup_passwd_loc} >& 2 return 99 fi else # : Even though the AIX passwd command is claimed to be linked : to clpasswd, we do not have an executable saved backup file. : the AIX passwd command is nowhere to be found. # nls_msg -l $cspoc_tmp_log ${_MSET} 999999 "${_CMD_NAME}: ERROR! ${bkup_passwd_loc} is either missing or does not appear to contain a copy of the AIX passwd command.\n" ${_CMD_NAME} ${bkup_passwd_loc} >& 2 return 99 fi else # : $config_file contents do not make sense or do not appear to : match the actual state of the AIX passwd command. Collect some : information on the state to allow later analysis. # nls_msg -l $cspoc_tmp_log ${_MSET} 9999 "${_CMD_NAME}: config files may be corrupted" ${_CMD_NAME} >& 2 [[ -s $config_init ]] && cat $config_init | sed "s|^|$config_init |" >> $LOG_FILE [[ -s $config_file ]] && cat $config_file | sed "s|^|$config_file |" >> $LOG_FILE ls -l ${bkup_passwd_loc} 2>&1 >> $LOG_FILE ls -l /usr/bin/passwd 2>&1 >> $LOG_FILE # : Check to see if the AIX passwd command is usable # if [[ -x /usr/bin/passwd && "l" != $(ls -l /usr/bin/passwd | cut -c1) ]] then sum /usr/bin/passwd | read actual_checksum rest ls -l /usr/bin/passwd | read skip skip skip skip actual_size rest if (( $inventory_size == $actual_size && $inventory_checksum == $actual_checksum )) then # : The AIX passwd command is not linked to the PowerHA clpasswd command : or any thing else. And the AIX passwd command looks valid. : So, should be able to run it from its normal location. # if (( ROOT == 1 )) then Command="passwd $D_UserName" passwd $D_UserName else Command="/usr/bin/su - $D_UserName -c \"passwd $D_UserName\"" /usr/bin/su - $D_UserName -c "passwd $D_UserName" fi else nls_msg -l $cspoc_tmp_log ${_MSET} 999999 "${_CMD_NAME}: ERROR! /usr/bin/passwd does not appear to contain a copy of the AIX passwd command.\n" ${_CMD_NAME} /usr/bin/passwd >& 2 return 99 fi elif [[ -x $bkup_passwd_loc && "l" != $(ls -l $bkup_passwd_loc | cut -c1) ]] then # : There is a saved backup copy of the real passwd comamnd. : Use that. # sum $bkup_passwd_loc | read bkup_checksum rest ls -l $bkup_passwd_loc | read skip skip skip skip bkup_size rest if (( $inventory_size == $bkup_size && $inventory_checksum == $bkup_checksum )) then if (( ROOT == 1 )) then # : Store the command to be executed in the Command for logging : Let the result of execution be captured in PASSWD_RETCODE, to correctly return the result. # Command="$bkup_passwd_loc $D_UserName" $bkup_passwd_loc $D_UserName else Command="/usr/bin/su $D_UserName -c \"$bkup_passwd_loc $D_UserName\"" /usr/bin/su $D_UserName -c "$bkup_passwd_loc $D_UserName" fi else nls_msg -l $cspoc_tmp_log ${_MSET} 999999 "${_CMD_NAME}: ERROR! ${bkup_passwd_loc} does not appear to contain a copy of the AIX passwd command.\n" ${_CMD_NAME} ${bkup_passwd_loc} >& 2 return 99 fi else nls_msg -l $cspoc_tmp_log ${_MSET} 14 "${_CMD_NAME}: ERROR! The AIX \"passwd\" command is either missing or not executable and could not be restored from the intended backup copy ${bkup_passwd_loc}.\n" ${_CMD_NAME} ${bkup_passwd_loc} >& 2 return $rc fi fi fi else # : Explicitly given a password. Use 'chpasswd' to change the password # # Again note the assumption that the local node is part of _TARGET_NODES # D_PassWord=$(print $PassWord | cldecodearg) if [[ $mode == "LDAP" ]] then # : LDAP password management # Command="print ${D_UserName}:${D_PassWord} | chpasswd -R LDAP" print "${D_UserName}:${D_PassWord}" | chpasswd -R LDAP else # : Traditional local password management # Command="print ${D_UserName}:${D_PassWord} | chpasswd" print "${D_UserName}:${D_PassWord}" | chpasswd fi fi PASSWD_RETCODE=$? # : Log the local command # if (( $PASSWD_RETCODE == 0 )) then result="success" else result="Failed (RC=$PASSWD_RETCODE)" fi print -- "$(date +'%D %T') ${LOCAL_NODE}: ${result}: $Command" >> $cspoc_tmp_log (( $PASSWD_RETCODE != 0 )) && return $PASSWD_RETCODE # : Run clnochpasswd utility on local node to unset ADMCHG flag if "-k" flag used # if (( $KEEP == 1 )) then clnochpasswd $D_UserName print -- "$(date +'%D %T') ${LOCAL_NODE}: success: clnochpasswd $D_UserName" >> $cspoc_tmp_log fi if [[ $mode != "LDAP" ]] then # : Run the clgetpasswd utility on the local node to retrieve new passwd info. # New_PassWord_info=$(clgetpasswd $D_UserName) if [[ -z $New_PassWord_info ]] then nls_msg -l $cspoc_tmp_log ${_MSET} 6 "${_CMD_NAME}: Error can not retrieve password for User ${D_UserName} from local node ${localnode}\n" ${_CMD_NAME} ${D_UserName} ${localnode} >& 2 return 1 fi # : encode the new password information # PW_remote_cmd=$(print "clpasswdremote $New_PassWord_info" | clencodearg -e) # : Run chpasswdremote across the other cluster nodes to propagate the password # OTHER_NODES=$(print $_TARGET_NODES | tr ',' '\n' | grep -vx $LOCAL_NODE | paste -d, -s - ) cel_f2 # : If /etc/security/passwd is part of a file collection, update the file : modification time, to make sure that file collections on all nodes knows : of this update, and does not backwards propagate it. # fc_name=$(clodmget -q "name = /etc/security/passwd" -f fc_name -n HACMPfcfile) if [[ -n $fc_name ]] then prop_auto=$(clodmget -q "name = $fc_name" -f prop_auto -n HACMPfilecollection) if [[ $prop_auto == "1" ]] then # : If /etc/security/passwd is part of a file collection that has : automatic propagation, update the file collection mod time to : the change just put in. # export MISSING_NODES="" chk_cmd=$(print 'clodmget -q "name = /etc/security/passwd" HACMPfcmodtime' | clencodearg -e) cel_f3 updt_nodes=$(cut -f1 -d: $try_out | paste -s -d, -) if [[ -s $try_err ]] then cat $try_err fi # : The constructed command below will extract the modification time : of /etc/security/passwd, add 1 to it to allow for slop, and : set the file collection modification time to that value # updt_modtime_cmd='print "HACMPfcmodtime:\\n\\tname = /etc/security/passwd\\n\\tmodtime = "$(( 1 + $(perl -le "print((stat shift)[9])" /etc/security/passwd) )) | odmchange -q "name = /etc/security/passwd" -o HACMPfcmodtime' E_updt_modtime_cmd=$(print -- "$updt_modtime_cmd" | clencodearg -e) cel_f4 fi fi return ${try_rc} fi