#!/bin/ksh
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# bos72X src/bos/usr/lib/nim/methods/c_function.sh 1.12 
#  
# Licensed Materials - Property of IBM 
#  
# Restricted Materials of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2012,2022 
# 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 
# @(#)03    1.12  src/bos/usr/lib/nim/methods/c_function.sh, cmdnim, bos72X, x2022_09A3  2/22/22  12:26:14
#
##############################################################################
#
# c_function 
#
# When adding new function, edit the following section
# USAGE string    - Update usage for the new function
# parse_attr()    - Add the new function to the case statement
# ck_attrs()      - Add the new function to the case statement
# run_operation() - Add the new function to the case statement
# <new_function>  - Add the new function definition to the script
#
###############################################################################
 
NIMPATH=${0%/*}
NIMPATH=${NIMPATH%/*}
[[ ${NIMPATH} = ${0} ]] && NIMPATH=/usr/lpp/bos.sysmgt/nim
NIM_METHODS="${NIMPATH}/methods"
. ${NIM_METHODS}/c_sh_lib

typeset COMMAND=`basename $0`
typeset operation=""
typeset ARGUMENT=""

# Search by parse_attr_ass and ck_attrs
typeset REQUIRED_ATTRS=""

# Search by parse_attr_ass
typeset OPTIONAL_ATTRS="client"

# Search by ck_attrs
typeset EXCLUSIVE_ATTRS=""

typeset USAGE="$COMMAND -o operation [-v] [-a <attribute>=<value> ... ] [ ARGUMENT ]\n\
\n\
Note: The presence of attribute and ARGUMENT depend on the operation being invoked.\n\
\n\
operation: \n\
        secure_tftp_access - add \"allow:/tftpboot\" to /etc/tftpaccess.ctl\n\
        run_hmcauth - get dpasswd file from management server for the client and call hmcauth\n\
	setup_dns_resource - check system for resolv.conf and (if found) define temp resource\n\
	setup_file_resource - check system for list of file(s) and (if found) define temp resource\n\
\n\
Syntax:\n\
        $COMMAND -o secure_tftp_access\n\
        $COMMAND -o run_hmcauth -a client=<client>\n\
        $COMMAND -o setup_dns_resource -a client=<client>\n\
        $COMMAND -o setup_file_resource -a client=<client>\n\
"

#-------------------------------------------------------------------------------
#
# NAME: parse_attr
#
# PARAMETERS: None
#
# DESCRIPTION: Check if the correct argument is being passed for the operation.
#              The function calls parse_attr_ass to check on the two variables:
#                 REQUIRED_ATTRS
#                 OPTIONAL_ATTRS
#              New function should define the two variables if the
#              function requires checking for REQUIRED_ATTRS and OPTIONAL_ATTRS.
#
# RETURNS: (int)
#              0 = success
#	       1 = failure
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
parse_attr() {

	typeset rc=0

	case $operation in

		"secure_tftp_access")
			;;
		"run_hmcauth")
			;;
		"setup_dns_resource")
			;;
		"setup_file_resource")
			;;
		*)
			echo "$USAGE"
			rc=1
			;;
	esac

	parse_attr_ass "${OPTARG}"
	rc=$?

	return $?
}

#-------------------------------------------------------------------------------
#
# NAME: check_attrs
#
# PARAMETERS: None
#
# DESCRIPTION: Check if the correct argument is being passed for the operation
#              The function calls ck_attrs to check on the two variables:
#                 REQUIRED_ATTRS
#                 EXCLUSIVE_ATTRS
#              New function should define the two variables if the
#              function requires checking for REQUIRED_ATTRS and EXCLUSIVE_ATTRS.
#
# RETURNS: (int)
#              0 = success
#              1 = failure
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
check_attrs() {

	typeset rc=0

	[[ -z $operation ]] && echo "$USAGE" && return 1

	case $operation in

		"secure_tftp_access")
			;;

		"run_hmcauth")
			if [[ -z $client ]]; then
				#
				# 0042-021 <c_function>: the "<client>" attribute is required for this
				# 	operation
				error ${ERR_MISSING_ATTR} "client"
			fi
			;;

		"setup_dns_resource")
			if [[ -z $client ]]; then
				#
				# 0042-021 <c_function>: the "<client>" attribute is required for this
				# 	operation
				error ${ERR_MISSING_ATTR} "client"
			fi
			;;

		"setup_file_resource")
			if [[ -z $client ]]; then
				#
				# 0042-021 <c_function>: the "<client>" attribute is required for this
				# 	operation
				error ${ERR_MISSING_ATTR} "client"
			fi
			;;
		*)
			echo "$USAGE"
			rc=1
			;;
	esac

	#
	# NOTE:
	# When adding new function, if ARGUMENT is not needed, 
	# then add the operation to be checked.
	#
	if [[ $operation = "secure_tftp_access" ]] &&
	   [[ -n $ARGUMENT ]]; then
		echo "$USAGE"
		return 1
	fi

	ck_attrs
	rc=$?

	return $rc
}

#-------------------------------------------------------------------------------
#
# NAME: run_operation
#
# PARAMETERS: None
#
# DESCRIPTION: Call the function based on the operation.
#              New function should be defined in the case statement. 
#
# RETURNS: (int)
#              0 = success
#              1 = failure
# OUTPUT: None
#
#-------------------------------------------------------------------------------
run_operation() {

	case $operation in
		"secure_tftp_access")
			secure_tftp_access
			return $?
			;;
		"run_hmcauth")
			run_hmcauth
			return $?
			;;
		"setup_dns_resource")
			download_dns_copy
			return $?
			;;
		"setup_file_resource")
			download_file_copy
			return $?
			;;
		*)
			echo "$USAGE"
			return 1
			;;
	esac

	return 0
}

#-------------------------------------------------------------------------------
#
# NAME: secure_tftp_access
#
# FUNCTION:
#               Modifies or creates /etc/tftpaccess.ctl to ensure tftp access
#               is not granted to more than /tftpboot as a result of SPOT
#               creation on a machine in the NIM env.
#
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#               -- uncomments #allow:/tftpboot
#               -- comments out deny:/tftpboot
#               -- when looking for the above, assumes that whitespace may
#                  have been inserted anywhere in the line (even though this
#                  is illegal).
#               -- creates file if does not exist
#               -- changes/sets permissions on /etc/tftpaccess.ctl
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#               global:
#                       tftp_enabled_by_nim     = tells if NIM uncommented the
#                                                 tftp entry in /etc/inetd.conf
#
# RETURNS: (int)
#		None
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
function secure_tftp_access {

	typeset has_access=""
	typeset has_nimstr=""
	typeset ALLOW=""
	typeset DENY=""

	# if file already exists...
	if [[ -s ${TFTPACCESS} ]]; then

		# is there a "deny" for /tftpboot that needs commenting out?
		DENY="^[	]*deny:[	]*\/tftpboot[	]*$"
		${GREP} -qE "${DENY}" ${TFTPACCESS}
		if [[ $? -eq 0 ]]; then
			# comment it out
			${SED} "s/${DENY}/#deny:\/tftpboot/g" ${TFTPACCESS} \
				> ${TMPDIR}/.tftpaccess.ctl.$$
			[[ $? -eq 0 ]] && \
				${MV} ${TMPDIR}/.tftpaccess.ctl.$$ ${TFTPACCESS}
		fi

		# is there an "allow" for /tftpboot that needs uncommenting?
		ALLOW="^[	]*#[	]*allow:[	]*\/tftpboot[	]*$"
		${GREP} -qE "${ALLOW}" ${TFTPACCESS}
		if [[ $? -eq 0 ]]; then
			# uncomment the allow
			${SED} "s/${ALLOW}/allow:\/tftpboot/g" ${TFTPACCESS} \
				> ${TMPDIR}/.tftpaccess.ctl.$$
			[[ $? -eq 0 ]] && \
				${MV} ${TMPDIR}/.tftpaccess.ctl.$$ ${TFTPACCESS}
			has_access=1
		else
			# is there already access to /tftpboot?
			${GREP} -E "^allow:\/tftpboot$" ${TFTPACCESS} >/dev/null 2>&1
			[[ $? -eq 0 ]] && has_access=1
		fi

		# is there an "# NIM access for network boot" string
		${GREP} -qE "# NIM access for network boot" ${TFTPACCESS}
		if [[ $? -eq 0 ]]; then
			has_nimstr=1
		fi
	fi

	# append to file if necessary (or create if not there)
	# add access for /tftpboot
	[[ -z "${has_nimstr}" ]] && echo "# NIM access for network boot" >> ${TFTPACCESS}
	[[ -z "${has_access}" ]] && echo "allow:/tftpboot" >> ${TFTPACCESS}

	# make sure permissions and owner are correct
	${CHMOD} 644 ${TFTPACCESS}
	${CHOWN} root.system ${TFTPACCESS}

	return 0
}

#-------------------------------------------------------------------------------
#
# NAME: run_hmcauth
#
# FUNCTION:
#               Get the dpasswd file of the managed system managing the client.
#               Call dkeyexch with a hidden -S flag to save the temporary decrypted
#               password file.  Using the data from the dpasswd file, calll
#               hmcauth (or pvcauth) to obtain client access to its managed system.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#               global:
#				client = NIM client object
#
# RETURNS: (int)
#               None
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
function run_hmcauth {

	typeset rc=1
	typeset managed_type=""
	typeset managed_system=""
	typeset managed_port=""
	typeset managed_hostname=""
	typeset passwd_file=""
	typeset decrypt_file=""
	typeset project=""

	managed_system=`$LSNIM -a mgmt_profile1 $client 2>/dev/null | $AWK '(NR==2) {print $3}'`

	if [[ -z $managed_system ]]; then
		#
		# 0042-021 <c_function>: the "<mgmt_profile>" attribute is required for this
		# 	operation
		#
		error ${ERR_MISSING_ATTR} "mgmt_profile"
	fi

	managed_type=`$LSNIM -l $managed_system 2>/dev/null | $GREP "type " | $AWK '{print $3}'`
	managed_hostname=`$LSNIM -a if1 $managed_system 2>/dev/null | $AWK '(NR==2) {print $4}'`
	passwd_file=`$LSNIM -a passwd_file $managed_system 2>/dev/null | $AWK '(NR==2) {print $3}'`

	if [[ -e $passwd_file ]]; then
		# Check the validity of the managed system
		# (also rebuilds connection defaults for powervc)
		$NIM -o check $managed_system

		# Call dkeyexch to decrypt the passwd_file
		decrypt_file=`/usr/bin/dkeyexch -f $passwd_file -I $managed_type -H $managed_hostname -S 2>/dev/null | $GREP "/var/ibm/sysmgt/dsm/tmp"`
        else
		# error file doesn't exist (DNE)
                error $ERR_ENOENT $passwd_file
        fi

	if [[ $managed_type = powervc ]]; then
		project=`$LSNIM -a project $managed_system 2>/dev/null | $AWK '(NR==2) {print $3}'`
	fi

	# determine the management service port
	# -must call after validity check (above)
	managed_port=`$LSNIM -a mgmt_port $managed_system 2>/dev/null | $AWK '(NR==2) {print $3}'`

	if [[ -n $decrypt_file ]]; then
		# Get the user and password from the decrypted file
		typeset user=`$CAT $decrypt_file | $AWK '{print $1}'`
		typeset passwd=`$CAT $decrypt_file | $AWK '{print $2}'`
		$RM $decrypt_file 2>/dev/null

		# Call hmcauth/pvcauth via c_rsh against the client
		if [[ -n $user ]] && [[ -n $passwd ]] && [[ -n $managed_hostname ]]; then
			connect=`$LSNIM -a connect $client 2>/dev/null | $AWK -F"=" '(NR==2) {print $2}'`
			[[ $managed_type = powervc ]] &&
				authcmd="/usr/sbin/pvcauth" ||
				authcmd="/usr/sbin/hmcauth"

			if [[ ${connect} = *secure* ]]; then
				$C_RSH -e $client "$authcmd ${project:+-o $project} -u $user -p '$passwd' -a $managed_hostname -P $managed_port >/dev/null 2>&1"
			else
				$C_RSH $client "$authcmd ${project:+-o $project} -u $user -p '$passwd' -a $managed_hostname -P $managed_port >/dev/null 2>&1"
			fi
			rc=$?
			if [[ $rc -ne 0 ]]; then
				#
				# 0042-106 <c_function>: unable to execute the "</usr/sbin/hmcauth>" program
				#
				error ${ERR_EXEC} $authcmd
			fi
		fi
	fi

	return $rc
}

#-------------------------------------------------------------------------------
#
# NAME: download_dns_copy
#
# FUNCTION:
#               Check client for existence of file /etc/resolv.conf
#               If found, a copy will be downloaded into a default
#               location.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#               global:
#				client = NIM client object
#
# RETURNS: (int)
#               None
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
function download_dns_copy {

	typeset -i rc=1

	typeset hostname=""
	typeset resource_path="/export/nim/${client}/tmp$$"
	typeset filename="${resource_path}/resolv.conf"
	typeset objname="resolv_tmp$$"


	# use primary interface for remote service request
	hostname=`$LSNIM -a if1 $client 2>/dev/null | $AWK '(NR==2) {print $4}'`

	if [[ -z $hostname ]]; then
		error ${ERR_MISSING_ATTR} "if1"
	fi

	# check for file existence
	rc=`${C_RSH} $hostname "\"/usr/bin/test -f /etc/resolv.conf && print 0 || print 1\""`

	if [[ $rc -ne 0 ]]; then
		# File DNE - exit from function
		return $rc
	fi

	# file exists, download into global (predefined) path
	$MKDIR -p $resource_path

	rc=`${C_RSH} $hostname "cat /etc/resolv.conf" > $filename`

	if [[ $rc -ne 0 ]]; then
		# Download failed or invalid - remove content
		$RM -r $resource_path 2>/dev/null
		return $rc
	fi

	# download complete - define resource
	rc=`create_dns_resource $filename $objname`

	if [[ $rc -ne 0 ]]; then
		# Creation failed or invalid - remove content
		$RM -r $resource_path 2>/dev/null
		return $rc
	fi

	# mark for deletion upon dealloc (disregard errors)
	$NIM -o change -a sync_required=destroy $objname >/dev/null 2>&1 

	print "$objname"

	return $rc
}

#-------------------------------------------------------------------------------
#
# NAME: create_dns_resource
#
# FUNCTION:
#               Defines a name resolution resource (based on existing client data)
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#				filename = FQ path to resolv.conf file
#				object   = name of resource
#               global:
#				client = NIM client object
#
# RETURNS: (int)
#               None
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
function create_dns_resource {

	typeset rc=1
	typeset filename="$1"
	typeset object="$2"


	if [[ -z "$filename" ]] || [[ -z "$object" ]]; then
		# Cannot create resource
		return 1
	fi

	# Check if object exists
	$LSNIM $object >/dev/null 2>&1 

	if [[ $? -eq 0 ]]; then
		# Cannot create resource
		return 1
	fi

	$NIM -o define -t resolv_conf -a server=master -a location=$filename $object >/dev/null 2>&1 

	return $?
}

#-------------------------------------------------------------------------------
#
# NAME: download_file_copy
#
# FUNCTION:
#               Check client for existence of file(s) referenced in $NIM_COPY_FILE
#               If found, a copy will be downloaded into a default location.
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#               global:
#				client = NIM client object
#
# RETURNS: (int)
#               None
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
function download_file_copy {

	typeset -i rc=1

	typeset hostname=""
	typeset content=""
	typeset filename=""
	typeset default_file="/tmp/._nim_${client}_tmp$$"
	typeset resource_path="/export/nim/${client}/tmp$$"
	typeset objname="file_tmp$$"


	# use primary interface for remote service request
	hostname=`$LSNIM -a if1 $client 2>/dev/null | $AWK '(NR==2) {print $4}'`

	if [[ -z $hostname ]]; then
		error ${ERR_MISSING_ATTR} "if1"
	fi

	# if NIM_COPY_FILE hasn't been set, then define
	# a file with single entry of /etc/hosts
	content=`env NIM_COPY_FILE 2>/dev/null`
	if [[ $? -ne 0 ]] || [[ ! -s "$content" ]]; then
		# copy default content and set var
		content=$default_file
		echo "/etc/hosts" > $content
	fi

	while read VAR
	do
		[[ -z ${VAR} ]] && continue

		# check for file existence
		rc=`${C_RSH} $hostname "\"/usr/bin/test -f ${VAR} && print 0 || print 1\""`

		[[ $rc -ne 0 ]] && continue

		# file exists, download into global (predefined) path
		filename="${resource_path}/${VAR}"

		$MKDIR -p `$DIRNAME "${filename}"`

		rc=`${C_RSH} $hostname "cat ${VAR}" > $filename`

		if [[ $rc -ne 0 ]]; then
			# Download failed or invalid - remove content
			$RM -r $resource_path 2>/dev/null
			return $rc
		fi
		# uncertainty on the mode, set a default
		$CHMOD 644 $filename

	done < $content

	# download complete - define resource
	rc=`create_file_resource $resource_path $objname`

	if [[ $rc -ne 0 ]]; then
		# Creation failed or invalid - remove content
		[[ -s "$default_file" ]] && $RM -r $default_file 2>/dev/null
		$RM -r $resource_path 2>/dev/null
		return $rc
	fi

	# remove tmp default content file
	[[ -s "$default_file" ]] && $RM -r $default_file 2>/dev/null

	# mark for deletion upon dealloc (disregard errors)
	$NIM -o change -a sync_required=destroy $objname >/dev/null 2>&1 

	print "$objname"

	return $rc
}

#-------------------------------------------------------------------------------
#
# NAME: create_file_resource
#
# FUNCTION:
#               Defines a file copying resource (based on existing client data)
#
# EXECUTION ENVIRONMENT:
#
# NOTES:
#
# RECOVERY OPERATION:
#
# DATA STRUCTURES:
#               parameters:
#				path	= FQ path to root directory
#				object	= name of resource
#               global:
#				client = NIM client object
#
# RETURNS: (int)
#               None
#
# OUTPUT: None
#
#-------------------------------------------------------------------------------
function create_file_resource {

	typeset rc=1
	typeset path="$1"
	typeset object="$2"


	if [[ -z "$path" ]] || [[ -z "$object" ]]; then
		# Cannot create resource
		return 1
	fi

	# Check if object exists
	$LSNIM $object >/dev/null 2>&1 

	if [[ $? -eq 0 ]]; then
		# Cannot create resource
		return 1
	fi

	$NIM -o define -t file_res -a server=master -a location=$path -a dest_dir=/ $object >/dev/null 2>&1 

	return $?
}

#-------------------------------------------------------------------------------
#
# NAME: c_function (main)
#
# PARAMETERS: -o operation=<operation>
#             [ -a <attribute1>=<value>
#               -a ...
#               -a <attributeN>=<valueN> ]
#             [ -v ]
#             [ ARGUMENT ]
#
# DESCRIPTION: Main for executing the operation.
#
# RETURNS: (int)
#              0 = success
#              1 = failure
# OUTPUT: None
#
#-------------------------------------------------------------------------------

typeset rc=0
typeset opset=0

while getopts :a:o:v c 
do
	case ${c} in
		a)	# validate the attr ass
			[[ $opset -eq 0 ]] && 
				echo "$COMMAND : please specify the operation with '-o' first.\n" &&
				exit 1
			parse_attr "${OPTARG}"
			[[ $? -ne 0 ]] && exit 1

			# include the assignment for use in this environment
			eval ${variable}=\"${value}\"
			;;

		o)	# operation
			opset=1
			operation=${OPTARG#*=}
			;;

		v)	# verbose mode (for debugging)
			set -x
			for i in $(typeset +f)
			do
				typeset -ft $i
			done
			;;

		\?)	# unknown option
			echo "$USAGE"
			exit 1
			;;
	esac
done

# Get the last argument
shift $((OPTIND - 1))
ARGUMENT=$*

# check for missing attrs
check_attrs
rc=$?

if [[ $rc -eq 0 ]]; then
	run_operation
	rc=$?
fi

exit $rc
