# /usr/local/smsd/lib_msgcmdexec.sh
#
# Author:	S.N. Thornton
# Date:		2010/02/28
# Modified:	2011/04/28	$Date: 2011/04/06 11:55:56 $
# Version:	2.0		$Id: lib_msgcmdexec.sh,v 1.4 2011/04/06 11:55:56 root Exp $
# Source:	$Source: /usr/local/smsd/RCS/lib_msgcmdexec.sh,v $
#
# If no parameters passed, then defaults to library mode, variables and functions defined
# if $1 = test	then print test messages
# or,
# if $1 = test and $2 = cmd, $3 = parms
#
# e.g.
#	lib_msgcmdexec.sh test			# prints test messages
#	lib_msgcmdexec.sh test cmd help		# prints help message
#	lib_msgcmdexec.sh test cmd getpower	# prints Power Status messages
#
# Note:
# If the variable SMS_EMAIL_ADDR is set it assumes it is called as part of the SMS server script
#
# Changelog
# 2011/04/28 Changed Auth to automatic if SMS used
# 2011/04/02 Add support for authentication (read/write)
# 2011/03/22 Major re-write to support multiple PDU devices
# 2011/03/21 Added changes to support Amazing PDU
# 2011/01/17 Allow help command even if IPpower not present
# 2010/07/23 Update sshallow to use sudo (doesn't work otherwise)
# 2010/06/21 Added sshallow to update firewall
# 2010/04/29 Changed fax/dsl on/off code into generic routine
# 2010/04/28 Added Help descriptions for Power devices
# 2010/04/28 Added dslon/off to target Cable modem
# 2010/03/10 Converted to proper library script and renamed
# 2010/02/28 Paramterised IP Power server IP/name and browser used to tx/rx commands
# 2010/02/09 Tidied up ping/pingcheck and added tracert
# 2010/02/09 Corrected help messages (was dumping [ in msgs)
# 2010/01/26 Added Ping check
# 2009/11/08 Added CMD processing capability for listed numbers
# 2008/10/27 Added support to log sms content
#


# Bugs
# 2011/03/27 cmd_pdustatus: Error messages from proxy are not detected correctly if http_proxy set (a system which is down returns a proxy error, not zero response which is what the code expects
#		Workaround: unset http_proxy before running (see header for this)
#		Fix: Change error handling code to filter out proxy errors
#

# ------ THE FOLLOWING VARIABLES CAN BE OVERRIDDEN IN THE CALLING SCRIPT ------

# Uncomment this to enable DEBUG when testing from cmd line  (no SQL write mode)
#DEBUG=1

# These contain info about the library and can be used to detect if it loaded

sLIBid=20110321											# Unique ID for Library
sLIBname="`echo $0 | sed 's/\-bash//'`"
if [ -z "${sLIBname}" ]; then sLIBname="/usr/local/bin/lib_msgcmdexec.sh"; fi			# Sanity Check
sLIBinfo[${sLIBid}0]="`basename ${sLIBname}`"							# Library Name
sLIBinfo[${sLIBid}1]="2.1"									# Library Version
sLIBinfo[${sLIBid}2]="Automation Library for PDUs from DGP (Amazing/Bravo) and Aviosys (IP Power 92xxx)"
sLIBinfo[${sLIBid}3]="Simon N. Thornton, 2011/03/24"						# Author/Date
sLIBinfo[${sLIBid}4]="`echo ${sLIBinfo[${sLIBid}0]/%.*/}.conf`"					# Name of library info files
sLIBinfo[${sLIBid}5]="`dirname ${sLIBname}`/${sLIBinfo[${sLIBid}4]} ~/${sLIBinfo[${sLIBid}4]}"	# Directories for library info files


# Cmd line browser to use
if [ -z "$iBROWSE" ]; 		then iBROWSE=1; fi
if [ -z "${aBROWSERS}" ]; 	then aBROWSERS=("w3m -dump" "curl -s -m 10" "links -source" "lynx -source"); fi
if [ -z "${sBROWSE}" ]; 	then sBROWSE=${aBROWSERS[${iBROWSE}]}; fi

# Capture date for use in log files
if [ -z "${sLogDATE}" ]; 	then sLogDATE=`date +"%Y-%m-%d %H:%M:%S"`; fi

#
# Do not remove SOT/EOT markers, content between is used for autogen inf file
# SOT #

#
# Disable use of proxy with PDUs (causes long delays if device is not available)
# Comment this out if you want it to go through your proxy
unset http_proxy

#
# List of passwords that allow RO access (separate multiple entries by spaces)
aIPPWR_AUTH_RO=(public)
# List of passwords that allow RW access (separate multiple entries by spaces)
aIPPWR_AUTH_RW=(private)

# Indicates if authorisation successful (null=no, RO=Read Only, RW=Read/Write)
# Comment out next line if you WANT password control, otherwise ignore
sIPPWR_AUTH_OK="RW"

#
# aIPPWR_PORT[xx]	= Port Name,		xx=Bank+Port
# aIPPWR_PORT_NIC[xx]	= Port Nickname,	xx=Bank+Port
# aIPPWR_PORT_DESC[xx]	= Port Description,	xx=Bank+Port
# aIPPWR_PORT_ON[xx]	= Turn on outlet,	xx=Bank+Port
# aIPPWR_PORT_OFF[xx]	= Turn off outlet,	xx=Bank+Port
#
# aBANK_PORTS[yy]	= List of Ports/dev,	yy=Bank
#

# The only fields in the Port config you can change are:
# aIPPWR_PORT_NIC[${iBANK}0]="P${iBANK}0"	Change P${iBANK}0 to a short nickname for the port
# aIPPWR_PORT_DESC[${iBANK}0]="Unused"		Change "Unused" to a description of the port

# ----------------------------------------------------------------------------
#
# DGP Amazing PDU
#
iBANK=0						# PDU Bank
iBANK_PORTS=8					# Number of ports
aBANK_PORTS[${iBANK}]="`seq -f \"%02g\" -s \" \" ${iBANK}0 ${iBANK}$((iBANK_PORTS-1))`"

aIPPWR_SVR_ADD[${iBANK}]="172.16.1.21"		# IP Address/Hostname of Device
aIPPWR_USR_PWD[${iBANK}]="changethis"		# Normal Password
aIPPWR_USR_PWDDEF[${iBANK}]="1234"		# Default Password
aIPPWR_SVR_TYP[${iBANK}]="dgp"			# Device type or manufacturer
aIPPWR_SVR_NAM[${iBANK}]="Amazing PDU"		# Device Description
aIPPWR_SVR_PORTS[${iBANK}]="${iBANK_PORTS}"	# Number of ports on Device
aIPPWR_USR[${iBANK}]="snmp"			# Username
aIPPWR_SVR_STAT[${iBANK}]="unknown"		# Status of device

# Port configuration
aIPPWR_PORT[${iBANK}0]="P${iBANK}0"; aIPPWR_PORT_NIC[${iBANK}0]="P${iBANK}0";	aIPPWR_PORT_DESC[${iBANK}0]="Unused";	aIPPWR_PORT_ON[${iBANK}0]="10000000"; aIPPWR_PORT_OFF[${iBANK}0]=${aIPPWR_PORT_ON[${iBANK}0]}
aIPPWR_PORT[${iBANK}1]="P${iBANK}1"; aIPPWR_PORT_NIC[${iBANK}1]="P${iBANK}1";	aIPPWR_PORT_DESC[${iBANK}1]="Unused";	aIPPWR_PORT_ON[${iBANK}1]="01000000"; aIPPWR_PORT_OFF[${iBANK}1]=${aIPPWR_PORT_ON[${iBANK}1]}
aIPPWR_PORT[${iBANK}2]="P${iBANK}2"; aIPPWR_PORT_NIC[${iBANK}2]="P${iBANK}2";	aIPPWR_PORT_DESC[${iBANK}2]="Unused";	aIPPWR_PORT_ON[${iBANK}2]="00100000"; aIPPWR_PORT_OFF[${iBANK}2]=${aIPPWR_PORT_ON[${iBANK}2]}
aIPPWR_PORT[${iBANK}3]="P${iBANK}3"; aIPPWR_PORT_NIC[${iBANK}3]="P${iBANK}3";	aIPPWR_PORT_DESC[${iBANK}3]="Unused";	aIPPWR_PORT_ON[${iBANK}3]="00010000"; aIPPWR_PORT_OFF[${iBANK}3]=${aIPPWR_PORT_ON[${iBANK}3]}
aIPPWR_PORT[${iBANK}4]="P${iBANK}4"; aIPPWR_PORT_NIC[${iBANK}4]="P${iBANK}4";	aIPPWR_PORT_DESC[${iBANK}4]="Unused";	aIPPWR_PORT_ON[${iBANK}4]="00001000"; aIPPWR_PORT_OFF[${iBANK}4]=${aIPPWR_PORT_ON[${iBANK}4]}
aIPPWR_PORT[${iBANK}5]="P${iBANK}5"; aIPPWR_PORT_NIC[${iBANK}5]="P${iBANK}5";	aIPPWR_PORT_DESC[${iBANK}5]="Unused";	aIPPWR_PORT_ON[${iBANK}5]="00000100"; aIPPWR_PORT_OFF[${iBANK}5]=${aIPPWR_PORT_ON[${iBANK}5]}
aIPPWR_PORT[${iBANK}6]="P${iBANK}6"; aIPPWR_PORT_NIC[${iBANK}6]="P${iBANK}6";	aIPPWR_PORT_DESC[${iBANK}6]="Unused";	aIPPWR_PORT_ON[${iBANK}6]="00000010"; aIPPWR_PORT_OFF[${iBANK}6]=${aIPPWR_PORT_ON[${iBANK}6]}
aIPPWR_PORT[${iBANK}7]="P${iBANK}7"; aIPPWR_PORT_NIC[${iBANK}7]="P${iBANK}7";	aIPPWR_PORT_DESC[${iBANK}7]="Unused";	aIPPWR_PORT_ON[${iBANK}7]="00000001"; aIPPWR_PORT_OFF[${iBANK}7]=${aIPPWR_PORT_ON[${iBANK}7]}

# ----------------------------------------------------------------------------
#
# IP Power 9258
iBANK=6						# PDU
iBANK_PORTS=8					# Number of ports
aBANK_PORTS[${iBANK}]="`seq -f \"%02g\" -s \" \" ${iBANK}0 ${iBANK}$((iBANK_PORTS-1))`"

aIPPWR_SVR_ADD[${iBANK}]="172.16.1.18"		# IP Address/Hostname of Device
aIPPWR_USR_PWD[${iBANK}]="changethis"		# Normal Password
aIPPWR_USR_PWDDEF[${iBANK}]="12345678"		# Default Password
aIPPWR_SVR_TYP[${iBANK}]="ipp"			# Device type or manufacturer
aIPPWR_SVR_NAM[${iBANK}]="IP Power 9258"	# Device Description
aIPPWR_SVR_PORTS[${iBANK}]="${iBANK_PORTS}"	# Number of ports on Device
aIPPWR_USR[${iBANK}]="admin"			# Username
aIPPWR_SVR_STAT[${iBANK}]="unknown"		# Status of device

# Port configuration (for IP Power the ON/OFF ports do not change across the units)
aIPPWR_PORT[${iBANK}0]="P${iBANK}0"; aIPPWR_PORT_NIC[${iBANK}0]="P${iBANK}0";	aIPPWR_PORT_DESC[${iBANK}0]="Unused";			aIPPWR_PORT_ON[${iBANK}0]="P60=1"; aIPPWR_PORT_OFF[${iBANK}0]="P60=0"
aIPPWR_PORT[${iBANK}1]="P${iBANK}1"; aIPPWR_PORT_NIC[${iBANK}1]="P${iBANK}1";	aIPPWR_PORT_DESC[${iBANK}1]="Unused";			aIPPWR_PORT_ON[${iBANK}1]="P61=1"; aIPPWR_PORT_OFF[${iBANK}1]="P61=0"
aIPPWR_PORT[${iBANK}2]="P${iBANK}2"; aIPPWR_PORT_NIC[${iBANK}2]="P${iBANK}2";	aIPPWR_PORT_DESC[${iBANK}2]="Unused";			aIPPWR_PORT_ON[${iBANK}2]="P62=1"; aIPPWR_PORT_OFF[${iBANK}2]="P62=0"
aIPPWR_PORT[${iBANK}3]="P${iBANK}3"; aIPPWR_PORT_NIC[${iBANK}3]="P${iBANK}3";	aIPPWR_PORT_DESC[${iBANK}3]="Unused";			aIPPWR_PORT_ON[${iBANK}3]="P63=1"; aIPPWR_PORT_OFF[${iBANK}3]="P63=0"
aIPPWR_PORT[${iBANK}4]="P${iBANK}4"; aIPPWR_PORT_NIC[${iBANK}4]="P${iBANK}4";	aIPPWR_PORT_DESC[${iBANK}4]="Unused";			aIPPWR_PORT_ON[${iBANK}4]="P64=1"; aIPPWR_PORT_OFF[${iBANK}4]="P64=0"
aIPPWR_PORT[${iBANK}5]="P${iBANK}5"; aIPPWR_PORT_NIC[${iBANK}5]="P${iBANK}5";	aIPPWR_PORT_DESC[${iBANK}5]="Unused";			aIPPWR_PORT_ON[${iBANK}5]="P65=1"; aIPPWR_PORT_OFF[${iBANK}5]="P65=0"
aIPPWR_PORT[${iBANK}6]="P${iBANK}6"; aIPPWR_PORT_NIC[${iBANK}6]="P${iBANK}6";	aIPPWR_PORT_DESC[${iBANK}6]="Unused";			aIPPWR_PORT_ON[${iBANK}6]="P66=1"; aIPPWR_PORT_OFF[${iBANK}6]="P66=0"
aIPPWR_PORT[${iBANK}7]="P${iBANK}7"; aIPPWR_PORT_NIC[${iBANK}7]="P${iBANK}7";	aIPPWR_PORT_DESC[${iBANK}7]="Unused";			aIPPWR_PORT_ON[${iBANK}7]="P67=1"; aIPPWR_PORT_OFF[${iBANK}7]="P67=0"

# EOT #
# Do not remove EOT marker, used to autogen inf file

# ------ DO NOT CHANGE VARIABLES AFTER THIS POINT -------

# Check if override file exists can override all previous variables), looks in library and user home directories
for a in ${sLIBinfo[${sLIBid}5]}; do
	if [ -e ${a} ]; then
		. ${a}
	fi
done

#
# ---------- Functions -----------
#


#
# Function:	Create variable override file
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	none
# Returns:	sERR		= Error message (if any)
#
cmd_dumpvars () {
	local f

	f="`echo ${sLIBinfo[${sLIBid}5]} | cut -d' ' -f1`"		# Create master file
	printf "\n#\n# ----- Edit this file to customise information about the device\n#\n# Source\t: %s\n# Version\t: %s\n# Description\t: %s\n# Author\t: %s\n#\n" "${sLIBinfo[${sLIBid}0]}" "${sLIBinfo[${sLIBid}1]}" "${sLIBinfo[${sLIBid}2]}" "${sLIBinfo[${sLIBid}3]}" >$f
	cat ${sLIBname} | awk 'BEGIN{prn=0}/^\# SOT \#/{prn=1}/^\# EOT \#/{prn=0}{if (prn>0) printf "%s\n",$0}' >>$f
	chmod 600 $f
}


#
# Function:	Verify password
# Created:	2011/03/21
# Modified:	2011/04/02
# Params:	$1		Password to be checked
# Returns:	sERR		Error message (if any)
#		sOUT		RO/RW or null if not Authorised
#
cmd_chkauth () {
	local a

	sOUT="";sERR=""

	if [ -n "$1" ]; then			# Check RO passwords first
		for a in ${!aIPPWR_AUTH_RO[*]}; do
			if [ "$1" = "${aIPPWR_AUTH_RO[$a]}" ];then
				sOUT="RO"
				break
			fi
		done
		for a in ${!aIPPWR_AUTH_RW[*]}; do
			if [ "$1" = "${aIPPWR_AUTH_RW[$a]}" ];then
				sOUT="RW"
				break
			fi
		done
		if [ -n "${sOUT}" ]; then	# Password matched
			sIPPWR_AUTH_OK="${sOUT}"
		else				# No match
			sERR="Error: cmd_chkauth - bad password"
		fi
	fi
}


#
# Function:	Get Bank,Port Refence, Port number, setting from input
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	$1		Port, accepts in the form of 'P60' or 'P60=1' etc
#				Port is of form: Pbx where 'P' is mandatory prefix, 'b' is the bank (0-9), 'x' is the port (0-99)
# Returns:	iBANK		Bank (PDU)
#		iPORT		Port (Pxx)
#		iPORT_REF	two-byte port reference
#		iPORT_BIT	port number,0-x
#		iPORT_VAL	Port setting (if specified)
#		sERR		Error message (if any)
#
cmd_decodeport () {
	local ref1 ref2 sPORT sCMD1 sCMD2

	sERR="";iBANK=""; iPORT=""; iPORT_REF=""; iPORT_BIT="";iPORT_VAL=""

	if [ -n "$1" ]; then
		sPORT=`echo -n $1 | tr -s '=' ' '`		# Split value on =
		ref1=`echo -n "${sPORT}" | awk '{printf("%s",tolower($1))}'`
		ref2=`echo -n "${sPORT}" | awk '{if (NF>1) printf("%s",tolower($2))}'`
		if [ "${ref2}" = "${ref1}" ]; then ref2=""; fi	# No '=' in value

		case "$ref1" in
#			p[0-9]{1,3}*)					# Pxx=off
			p*)					# Pxx=off
				iBANK=`echo -n ${ref1} | cut -b2`	# 2nd char is Bank reference
				iPORT=`echo ${ref1} | tr '[a-z]' '[A-Z]'`				# Port name
				iPORT_REF=`echo -n ${ref1} | cut -b3-`	# Remaining chars are port references
				iPORT_BIT=`echo -n ${ref1} | awk '{printf "%s",substr($1,length($1))}'`	# Port number
				;;
			*on|*off)					# Turn devices on/off e.g. faxon/faxoff
				sCMD1="`echo ${ref1} | awk -Fo '{printf "%s",tolower($1)}'`"	# Device being controlled
				ref2="`echo ${ref1} | awk -Fo '{printf "o%s",tolower($2)}'`"	# off/on
				sOUT=""
				for a in ${!aIPPWR_PORT_NIC[*]}; do
					if [ "$sCMD1" = "${aIPPWR_PORT_NIC[$a]}" ];then
						sCMD1="${aIPPWR_PORT[$a]}"
						iBANK=`echo -n ${sCMD1} | cut -b2`	# 2nd char is Bank reference
						iPORT="${sCMD1}"			# Port name
						iPORT_REF=`echo -n ${sCMD1} | cut -b3-`	# Remaining chars are port references
						iPORT_BIT=`echo -n ${sCMD1} | awk '{printf "%s",substr($1,length($1))}'`	# Port number
						break
					fi
				done
				;;
		esac
		ref1=${sCMD1}

		case "$ref2" in
			0|off|OFF)	ref2=0
					;;
			1|on|ON)	ref2=1
					;;

			*)		ref2=""
					;;
		esac
		iPORT_VAL=$ref2

		if [ -n "$DEBUG" ]; then printf "%-16s:\tINPUT=\"%-10s\"\tBANK=%-3s\tPORT=%-3s\tPORT_REF=%-3s\tPORT_VAL=%-3s\n" "Port decode" "$1" "${iBANK}" "$iPORT" "$iPORT_REF" "$iPORT_VAL" 1>&2; fi

		if [ -z "${iBANK}" ]; then sERR="$sERR,iBANK";fi
		if [ -z "$iPORT_REF" ]; then sERR="$sERR,iPORT_REF"; fi
		if [ -z "$iPORT" ]; then sERR="$sERR,iPORT"; fi

		if [ -n "$sERR" ]; then
			sERR="Error: cmd_decodeport - $sERR could not be determined from input: $1"
		fi
	else
		sERR="Error: cmd_decodeport - no port specified when called"
	fi
}


#
# Function:	Initialise structures if not already setup
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	none
# Returns:	sERR	Error message (if devices not online)
#
cmd_pduinit () {
	if [ -n "$DEBUG" ]; then printf "%-16s:\t%s\tif >0 indicates already initialised\n" "cmd_pduinit" "${#aIPPWR_SVR_ON[*]}" 1>&2;fi
	if [ "${#aIPPWR_SVR_ON[*]}" -eq 0 ]; then
		cmd_pdustatus
	fi
}

#
# Function:	Reload all variables
# Created:	2011/05/03
# Modified:	2011/05/03
# Params:	none
# Returns:	sERR	Error message (if devices not online)
#
cmd_pduinitreset () {
	unset aIPPWR_SVR_ON
	cmd_pduinit
}



#
# Function:	Tests which PDUs are online and figures out active password
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	none
# Returns:	aIPPWR_SVR_ON	Array of indexes of active PDUs
#		sERR		Error messages
#
cmd_pdustatus () {
	local i iCNT pwd sCMDout

	sERR=""; iCNT=0
	unset aIPPWR_SVR_ON
	for i in ${!aIPPWR_SVR_ADD[*]}; do
		if [ -n "$DEBUG" ]; then printf "%-16s:\tHost\t%s\n" "cmd_pdustatus" "${aIPPWR_SVR_ADD[$i]}" 1>&2;fi

#		if [ "`${sBROWSE} http://${aIPPWR_SVR_ADD[$i]} 2>/dev/null | wc -c`" -ne 0 ]; then
		if [ -n "`nmap -sP ${aIPPWR_SVR_ADD[$i]} -oG - | grep Host:`"  ]; then
			aIPPWR_SVR_STAT[$i]="online"				# Indicate status
			aIPPWR_SVR_ON[$iCNT]=$i;iCNT=$((iCNT+1))		# Track which systems are online

			# Test if user or default password is in use (kludge in case it's reset to default)
			for pwd in "${aIPPWR_USR_PWDDEF[$i]}" "${aIPPWR_USR_PWD[$i]}"; do
				tmpPWD="$pwd"
				case "${aIPPWR_SVR_TYP[$i]}" in
					ipp)
						sCMDout="`${sBROWSE} http://${aIPPWR_USR[$i]}:${tmpPWD}@${aIPPWR_SVR_ADD[$i]}/Set.cmd?CMD=GetPower 2>/dev/null | grep -v "HTTPCMD_TRYLIMIT" | sed 's/<html>//g'`"
						if [ -n "${sCMDout}" ]; then	# It worked, use this password
							sCMDout=""
							break
						else
							tmpPWD=""		# It failed
						fi
						;;
					dgp)
						;;
				esac
			done
			if [ -n "$tmpPWD" ]; then
				aIPPWR_USR_PWD[$i]=${tmpPWD}
			else
				sERR="Error: cmd_pdustatus Wrong username/password for ${aIPPWR_SVR_NAM[$i]}"
				if [ -n "$DEBUG" ]; then printf "%s\n" "$sINP" 1>&2;fi
			fi
		else
			aIPPWR_SVR_STAT[$i]="offline"				# Indicate status
		fi
		if [ -n "$DEBUG" ]; then printf "%-16s:\t" "Device"; printf "%d\t%-15s\t%-7s\n" $i "${aIPPWR_SVR_ADD[$i]}" "${aIPPWR_SVR_STAT[$i]}" 1>&2; fi
	done

	if [ ${iCNT} -eq 0 ]; then	# Sanity check
		sERR="Error: cmd_pdustatus - no active devices found"
	fi
}


#
# Function:	Displays info about the PDUs
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	none
#
cmd_pduinfo () {
	local a p i
	(
		printf "%-16s:\t" "Browsers";	for a in ${!aBROWSERS[*]}; do		printf "\"%s\"\t" "${aBROWSERS[$a]}"; done;echo
		for a in ${!aIPPWR_SVR_ADD[*]}; do 	printf "%-16s:\t" "Device";	printf "%d\t%-15s\t%-7s\t%-11s\t%-6s\n" $a "${aIPPWR_SVR_ADD[$a]}" "${aIPPWR_SVR_TYP[$a]}" "${aIPPWR_SVR_NAM[$a]}" "${aIPPWR_USR[$a]}"; done
		for a in ${!aIPPWR_PORT[*]}; do 	printf "%-16s:\t" "Port";	printf "%s\t%s\t%-20s\t%-8s\t%-8s\n" "${aIPPWR_PORT[$a]}" "${aIPPWR_PORT_NIC[$a]}" "${aIPPWR_PORT_DESC[$a]}" "${aIPPWR_PORT_ON[$a]}" "${aIPPWR_PORT_OFF[$a]}"; done
		for a in ${!aBANK_PORTS[*]}; do 	printf "%-16s:\t" "Device Bank";printf "%02d\t%s\n" "$a" "${aBANK_PORTS[$a]}"; done

		cmd_pdustatus				# Find out what servers are online

		printf "\n%-16s:\t%s\n" "Active Devs" "${#aIPPWR_SVR_ON[*]}"
		for a in ${aIPPWR_SVR_ON[*]}; do
			printf "%-16s:\t" "Device";	printf "%d\t%-15s\t%-20s\t%-11s\t%-6s\n" $a "${aIPPWR_SVR_ADD[$a]}" "${aIPPWR_SVR_TYP[$a]}" "${aIPPWR_SVR_NAM[$a]}" "${aIPPWR_USR[$a]}"
			printf "%-16s:\t%s\n" "Ports" "${aBANK_PORTS[$a]}"
			for p in ${aBANK_PORTS[$a]}; do printf "%-16s:\t" "Port"; printf "%s/%s\t" $a $p; printf "%s\t%s\t%-20s\t%-8s\t%-8s\n" "${aIPPWR_PORT[$p]}" "${aIPPWR_PORT_NIC[$p]}" "${aIPPWR_PORT_DESC[$p]}" "${aIPPWR_PORT_ON[$p]}" "${aIPPWR_PORT_OFF[$p]}"; done
			cmd_getpower P${a}0
		done
	) 1>&2
}


#
# Function:	Displays description of ports
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	$1	PDU Port reference
# Returns:	sOUT	Port List
#
cmd_pduinfoports () {
	local a p

	if [ "${#aIPPWR_SVR_ON[*]}" -gt 0 ]; then
		sOUT="`for a in ${aIPPWR_SVR_ON[*]}; do \
				for p in ${aBANK_PORTS[$a]}; do \
					printf "%s\t%-20s\n" "${aIPPWR_PORT_NIC[$p]}" "${aIPPWR_PORT_DESC[$p]}"; \
				done; \
			done; \
		`"
	else
		sOUT="No PDU devices online"
	fi
}


#
# Function:	Get PDU port settings
# Created:	2011/03/21
# Modified:	2011/03/28
# Params:	$1	PDU Port reference (optional, defaults to port 0 on first PDU defined)
# Returns:	sOUT	String containg port settings
#		sERR	Error message (if any)
#
cmd_getpower () {
	local a i sCMDout b sBit sTMP sSRC

	sERR="";sCMDout=""
	if [ -n "$1" ]; then		# Display specified PDU
		sSRC="$@"
	else				# Display status of all PDUs online
		for i in ${aIPPWR_SVR_ON[*]}; do
			sSRC="P${i}0 ${sSRC}"
		done
	fi

	for b in ${sSRC}; do
		cmd_decodeport $b
		if [ -n "$DEBUG" ]; then printf "%-16s:\tINPUT=\"%-10s\"\tBANK=%-3s\tPORT=%-3s\tPORT_REF=%-3s\tPORT_VAL=%-3s\n" "Port get" "$b" "${iBANK}" "$iPORT" "$iPORT_REF" "$iPORT_VAL" 1>&2; fi

# Returns:	iBANK		Bank (PDU)
#		iPORT		Port (Pxx)
#		iPORT_REF	two-byte port reference
#		iPORT_BIT	port number,0-x
#		iPORT_VAL	Port setting (if specified)
#		sERR		Error message (if any)

		if [ -z "$sERR" ]; then		# Only continue if decode successful
			case "${aIPPWR_SVR_TYP[${iBANK}]}" in
				dgp)	sTMP="${sBROWSE} http://${aIPPWR_SVR_ADD[${iBANK}]}/control.cgi 2>/dev/null"
					if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
						sCMDout="`${sTMP} 2>/dev/null | grep "Z1" | cut -d'=' -f3 | cut -d',' -f1`"
						if [ -n "${sCMDout}" ]; then
							sOUT=""
							for i in `seq 1 ${aIPPWR_SVR_PORTS[${iBANK}]}`; do
								sBit=`echo $sCMDout | cut -b$i`
								sOUT="${sOUT} ${aIPPWR_PORT[${iBANK}$((i-1))]}=${sBit}"
							done
							sOUT="`echo $sOUT | sed 's/^ //'`"
						fi
					else
						sOUT="device offline"
					fi
					;;
				ipp)	sTMP="${sBROWSE} http://${aIPPWR_USR[${iBANK}]}:${aIPPWR_USR_NAM[${iBANK}]}@${aIPPWR_SVR_ADD[${iBANK}]}/Set.cmd?CMD=GetPower 2>/dev/null"
					if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
						sCMDout="`${sTMP} 2>/dev/null | sed 's/<html>//g;s/<\/html>//g'`"
						sOUT=$sCMDout
					else
						sOUT="device offline"
					fi
					;;
			esac
			if [ -n "$DEBUG" ]; then
				printf "%-16s:\t%s\n" "Port cmd" "${sTMP}" 1>&2
				if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
					printf "%16s:\t%s\n" "return" "${sOUT}" 1>&2
				fi
			fi
		fi
	done
}


#
# Function:	Set PDU port on/off
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	$1	Port on/off, Pxx=0|1
# Returns:	sOUT	String containg port settings
#		sERR	Error message (if any)
#
cmd_setpower () {
	local i sCMDout b sBit s sTMP sEXEC

	sERR="";sOUT=""
	if [ -z "$1" ]; then
		sERR="Error: cmd_decodeport - no port specified when called"
		exit
	fi

	for b in "$@"; do
		cmd_decodeport $b
		if [ -n "$DEBUG" ]; then printf "%-16s:\tINPUT=\"%-10s\"\tBANK=%-3s\tPORT=%-3s\tPORT_REF=%-3s\tPORT_VAL=%-3s\n" "Port set" "$b" "${iBANK}" "$iPORT" "$iPORT_REF" "$iPORT_VAL" 1>&2; fi

# Returns:	iBANK		Bank (PDU)
#		iPORT		Port (Pxx)
#		iPORT_REF	two-byte port reference
#		iPORT_BIT	port number,0-x
#		iPORT_VAL	Port setting (if specified)
#		sERR		Error message (if any)

		if [ -z "$sERR" ]; then		# Only continue if decode successful
			if [ -n "$iPORT_VAL" ]; then
				case "${aIPPWR_SVR_TYP[${iBANK}]}" in
					dgp)	case "$iPORT_VAL" in
							0|off)	s="${aIPPWR_PORT_OFF[$iPORT_REF]}"
								sEXEC="3?3"
								;;
							1|on)	s="${aIPPWR_PORT_ON[$iPORT_REF]}"
								sEXEC="1?3"
								;;
						esac
						sTMP="${sBROWSE} http://${aIPPWR_SVR_ADD[${iBANK}]}/${sEXEC}=${aIPPWR_USR[${iBANK}]},${aIPPWR_USR_PWD[${iBANK}]},${s}, 2>/dev/null"
						if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
							sOUT="`${sTMP}`"
						fi
						;;
					ipp)	sTMP="${sBROWSE} http://${aIPPWR_USR[${iBANK}]}:${aIPPWR_USR_PWD[${iBANK}]}@${aIPPWR_SVR_ADD[${iBANK}]}/Set.cmd?CMD=SetPower+${b} 2>/dev/null"
						if [ -z "$DEBUG" ]; then
							if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
								sOUT="`${sTMP} | sed 's/<html>//g;s/<\/html>//g'`"
							fi
						fi
						;;
				esac
				if [ -n "$DEBUG" ]; then
					printf "%-16s:\t%s\n" "Port cmd" "${sTMP}" 1>&2
				fi
			fi
			cmd_getpower $b
			if [ -n "$DEBUG" ]; then
				printf "%-16s:\t%s\n" "Port ret" "${sOUT}" 1>&2
			fi
		else
			if [ -n "$DEBUG" ]; then echo $sERR 1>&2; fi
		fi
	done
}


#
# Function:	Get PDU setup
# Created:	2011/03/21
# Modified:	2011/03/21
# Params:	$1	PDU Port reference (optional, defaults to port 0 on first PDU defined)
# Returns:	sOUT	String containg port settings
#		sERR	Error message (if any)
#
cmd_getsetup () {
	local i sCMDout b sBit sTMP sSRC iBROWSE

	iBROWSE=0				# Use w3m for this (index runs from 0, not 1)
	sERR="";sCMDout=""
	if [ -n "$1" ]; then		# Display specified PDU
		sSRC="$1"
	else				# Take first PDU in list
		iBANK="`echo ${aIPPWR_SVR_ON[*]} | cut -d' ' -f1`"
		sSRC="P${iBANK}0"
	fi

	sOUT=""
	for b in ${sSRC}; do
		cmd_decodeport $b
		if [ -n "$DEBUG" ]; then printf "%-16s:\tINPUT=\"%-10s\"\tBANK=%-3s\tPORT=%-3s\tPORT_REF=%-3s\tPORT_VAL=%-3s\n" "Port get" "$b" "${iBANK}" "$iPORT" "$iPORT_REF" "$iPORT_VAL" 1>&2; fi

# Returns:	iBANK		Bank (PDU)
#		iPORT		Port (Pxx)
#		iPORT_REF	two-byte port reference
#		iPORT_BIT	port number,0-x
#		iPORT_VAL	Port setting (if specified)
#		sERR		Error message (if any)

		if [ -z "$sERR" ]; then		# Only continue if decode successful
			case "${aIPPWR_SVR_TYP[${iBANK}]}" in
				dgp)	sTMP="${aBROWSERS[${iBROWSE}]} -cols 32 http://${aIPPWR_SVR_ADD[${iBANK}]}/ 2>/dev/null"		# Special, use w3m
					if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
						sCMDout="`${sTMP} 2>/dev/null  | grep -v "\-\-\-"| cut -b1-32 | sed -e 's/^ \{1,\}//' -e 's/ \{1,\}$//' -e 's/   //' | cut -b2- | grep -v \"^ \"`"
						if [ -n "${sCMDout}" ]; then
							sOUT="${sOUT}${sCMDout}"
						fi
					else
						sOUT="device offline"
					fi
					;;
				ipp)	sTMP="${sBROWSE} http://${aIPPWR_USR[${iBANK}]}:${aIPPWR_USR_NAM[${iBANK}]}@${aIPPWR_SVR_ADD[${iBANK}]}/Set.cmd?CMD=GetSetup 2>/dev/null"
					if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
						sCMDout="`${sTMP} 2>/dev/null | sed 's/<html>//g;s/<\/html>//g'`"
						if [ -n "${sCMDout}" ]; then
							sOUT="${sOUT}${sCMDout}"
						fi
					else
						sOUT="device offline"
					fi
					;;
			esac
			if [ -n "$DEBUG" ]; then
				printf "%-16s:\t%s\n" "Port cmd" "${sTMP}" 1>&2
				if [ "${aIPPWR_SVR_STAT[${iBANK}]}" = "online" ]; then
					printf "%-16s:\t%s\n" "  return" "${sOUT}" 1>&2
				fi
			fi
		fi
	done
}


#
# Function:	Exec commands passed via email or as cmd line params
# Created:	2011/02/28
# Modified:	2011/03/21
#
cmd_exec () {
	local sSMS sINP sCMD sCMDparm sTMP sCMD1 sCMD2 sCMD3 z

	# Check if we have been called by the SMS server script
	if [ -n "${SMS_EMAIL_ADDR}" ]; then
		if [ ! -e $1 ]; then
			return
		fi

		# Assume authenticated if called from SMS
#		if [ -z "${sIPPWR_AUTH_OK}" ]; then sIPPWR_AUTH_OK="RW"; fi
		sIPPWR_AUTH_OK="RW"

		sSMS="$1"
		sINP="`formail -I \"\" <${sSMS} | tr -d \"\'\'\"`"
	else
		sSMS=""
		sINP="$@"
	fi
	sCMDout=""

	cmd_pduinit			# Setup PDU info (just in case)

	while [ -n "${sINP}" ]; do
		sCMD=`echo "${sINP}" | awk '{printf "%s",tolower($1)}'`
		sCMDparm=`echo "${sINP}" | awk '{if (NF>1) { for (a=2;a<=NF;a++) printf "%s ",$(a)}}' | sed -e 's/ \{1,\}$//'`
		sINP=${sCMDparm}
		if [ -n "${DEBUG}" ]; then printf "%-16s:\tCMD=%s\tPARAM=\"%s\"\n" "cmd_exec" "${sCMD}" "${sCMDparm}" 1>&2; fi
		case "${sCMD}" in
			cmd|command)
				;;
			auth)
				sCMDparm=`echo "${sINP}" | awk '{printf "%s",tolower($1)}'`
				sINP=`echo "${sINP}" | awk '{if (NF>1) { for (a=2;a<=NF;a++) printf "%s ",$(a)}}' | sed -e 's/ \{1,\}$//'`
				cmd_chkauth "${sCMDparm}"
				if [ -n "${sERR}" ]; then	# Authorisation failed?
					sINP=""
				fi
				;;
			ifconfig)
				if [ -n "${sIPPWR_AUTH_OK}" ]; then		# Any authorisation allowed
					sCMDout="`${sCMD} ${sCMDparm}`"
				fi
				sINP=""			# End sequence
				;;
			getsetup)
				if [ -n "${sIPPWR_AUTH_OK}" ]; then		# Any authorisation allowed
					cmd_getsetup "${sCMDparm}"
					sCMDout="${sOUT}"
				fi
				sINP=""			# End sequence
				;;
			iftoggle)			# Toggle interface up/down
				if [ "${sIPPWR_AUTH_OK}"="RW" ]; then
					if [ -z "${sCMDparm}" ]; then sCMDparm=eth1; fi
					sCMDout="`sudo /sbin/ifdown ${sCMDparm}; sudo /sbin/ifup ${sCMDparm}`"
				fi
				sINP=""			# End sequence
				;;
			setpower)
				if [ "${sIPPWR_AUTH_OK}"="RW" ]; then
					for z in ${sCMDparm}; do
						cmd_setpower "${z}"
						sCMDout="${sOUT}"
						sleep 3
					done
				fi
				sINP=""			# End sequence
				;;
			*on|*off)			# Turn devices on/off e.g. faxon/faxoff
				if [ "${sIPPWR_AUTH_OK}"="RW" ]; then
					cmd_setpower "${sCMD}"
					sCMDout="${sOUT}"
				fi
				sINP=""			# End sequence
				;;

			sshallow)			# Update firewall access list
				if [ "${sIPPWR_AUTH_OK}"="RW" ]; then
					sCMDout="`sudo /usr/local/sbin/ssh_allow_me 2>&1 | awk '{if (NF>=5) printf(\"%s=%s\\n\",$3,$5)}'`"
				fi
				sINP=""
				;;
			getpower)
				cmd_getpower "${sCMDparm}"
				sCMDout="${sOUT}"
				sINP=""			# End sequence
				;;
			getports)
				cmd_pduinfoports "${sCMDparm}"
				sCMDout="${sOUT}"
				sINP=""			# End sequence
				;;
			ping|pingcheck)
				if [ -z "${sCMDparm}" ]; then sCMDparm="www.yahoo.com"; fi
				sCMDout="`ping -c 5 ${sCMDparm} | awk '/^PING/{printf(\"%s %s %s\n\",$1,$2,$3)}/ bytes from /{if ($(NF)==\"ms\") printf(\"%s %s ms\n\",$(NF-3),$(NF-1));if ($(NF)==\"Unreachable\") printf(\"%s %s %s %s\n\",$4,$5,$6,$7)}/packets transmitted/{printf(\"%s TX, %s RX, %s loss, time %s\n\",$1,$4,$6,$(NF))}'`"
				sINP=""
				;;
			tracert)
				if [ -z "${sCMDparm}" ]; then sCMDparm="www.yahoo.com"; fi
				sCMDout="`traceroute -n ${sCMDparm} | awk '/^traceroute to /{printf(\"trace to %s %s\n\",$3,$4)}{if ($(NF)==\"ms\") {printf(\"%02d %s\",$1,$2);for (a=4;a<=NF;a++) printf(" %s",$(a));printf("\n")};if ($2==\"*\") printf(\"%s\n\",$0)}'`"
				sINP=""
				;;
			help)
				sCMDout="`printf \"Commands available:\n\ncmd ifconfig {port}\ncmd iftoggle {port}\ncmd getpower\ncmd setpower Pxx={0|1}\ncmd getsetup\ncmd ping {HOST}\ncmd pingcheck\n        \nCommands (contd):\n\ncmd auth {passwd}\ncmd tracert {HOST}\ncmd fax|dsl{on|off}\ncmd sshallow\ncmd getports\n\n\"`"
				sINP=""			# End sequence
				;;
			error:)				# Internal command used to display error status
				sCMDout="${sCMD} ${sCMDparm}"
				sINP=""			# End sequence
				;;

			*)	sINP=""			# Invalid command, abort
				;;
		esac
		if [ -n "${sCMDout}" ]; then
			if [ -n "${DEBUG}" ]; then printf "%16s:\t%s\n" "output" "${sCMDout}" 1>&2; fi
			if [ -n "${SMS_EMAIL_ADDR}" ]; then
				sTMP=`mktemp /var/spool/sms/outgoing/cmd_XXXXXX`
				printf "To: +%s\n\n%s\n" "${FROM}" "${sCMDout}" >$sTMP
				chown sms:sms ${sTMP}; chmod 600 ${sTMP}
			else
				printf "%s\n" "${sCMDout}"
			fi
		fi
	done
}



# ---------- Main ----------------

# Initialise library
cmd_pduinit

# If $1 = "test" then exec test mode or commands following "test".
if [ "$1" = "test" ]; then
	shift
	(
		# Enable all auth from cmd line
		sIPPWR_AUTH_OK="RW"

		if [ -n "$1" ]; then
			if [ -n "${DEBUG}" ]; then
				printf "%-16s:\t%s\n" "cmd" "$@"
			fi
			cmd_exec $@
		else (
			cmd_pduinfo;echo
			cmd_exec command help;echo ;echo
			cmd_exec cmd getpower
			#cmd_exec cmd setpower P04=1 P04=0;echo
			cmd_exec cmd getsetup;echo
			cmd_exec cmd getports
			) 1>&2
		fi
	) 1>&2
fi

