#!/bin/ksh93

NB_VERSION="7.6.0.2"
PBX_VERSION="1.5.4.5"

# most important ports on which only root should be able to listen
PRIV_PORTS=( 1556 13722 13724 13782 )	# pbx, bpjava-msvc, vnetd, bpcd

PBX_CFG='/etc/vx/VxICS/VxPBX.cfg'
ICS_CFG='/etc/vx/VxICS/icsul.conf'
LOG_CFG='/etc/vx/vrtslog.conf'

PBX_PROD_ID='50936'
PBX_ORIG_ID='103'
NB_PROD_ID='51216'

NB_HOME="${.sh.file%/*/*}"
NB_BASE="${NB_HOME%/netbackup}"

PBX_LOG_DIR='/var/netbackup/logs/vxpbx'
L10N_LIB='libvxexticu.so.3'

SVC='svc:/application/backup/netbackup:vnetd'

RES_PATH="${NB_BASE}/resources"

# Per default this is /usr/share - but no-one needs SWID files
TAG_LOCATION='/etc/netbackup'

PROG="${.sh.file##*/}"

SVCCFG=/usr/sbin/svccfg
SVCPROP=/usr/bin/svcprop
SVCADM=/usr/sbin/svcadm

function writeConfig {
	typeset -n FINAL=$1 VALS=$2
	typeset CFG="$3" VERS="$4" ID=$5
	if [[ -z ${ID} ]]; then
		for KEY in ${!VALS[@]} ; do
			FINAL+="\"${KEY}\"=\"${VALS[${KEY}]}\"\n"
		done
	else
		# no quotes
		for KEY in ${!VALS[@]} ; do
			FINAL+="${ID}.${KEY}=${VALS[${KEY}]}\n"
		done
	fi
	if [[ ! -d  ${CFG%/*} ]]; then
		if ! ${PRINT} mkdir -p "${CFG%/*}" ; then
			print -u2 "# Unable to create directory '${CFG%/*}'."
			return 1
		fi
	fi
	if [[ ! -e "${CFG}-${VERS}.pre" ]]; then
		if [[ -r ${CFG} ]]; then
			${PRINT} cp -p "${CFG}" "${CFG}-${VERS}.pre" || return 2
		else
			${PRINT} touch "${CFG}-${VERS}.pre" || return 2
		fi
	fi

	if (( DRY )); then
		print "print -n '${FINAL}' >${CFG}"
	elif ! print -n "${FINAL}" >"${CFG}" ; then
		print -u2 '# Unable to write ${1%_CFG} config.'
		return 3
	fi
	return 0
}

# Create/Overwrite /etc/vx/VxICS/VxPBX.cfg by keeping unimportant known settings
# and adding/replacing the important stuff.
function updatePbxConfig {
	typeset -A VALS=(	# DEFAULTS
		[Insecure]=1 [AuthUsers]='root' [DebugLevel]=1
		[InstallPath]="${NB_HOME}/bin" [PortNumber]=1556
	)
	typeset KEY VAL LINE
	if [[ -r ${PBX_CFG} ]]; then
		while read LINE ; do
			[[ ! ${LINE:0:1} == '"' ]] && continue
			VAL=${LINE#*=}
			KEY=${.sh.match}
			VAL="${VAL:1:${#VAL}-2}"	# remove leading/trailing quotes
			KEY="${KEY:1:${#KEY}-3}"	# remove leading/trailing quotes and =
			[[ -z ${KEY} || -z ${VAL} || ${KEY} == 'InstallPath' || \
				-z ${VALS[${KEY}]} ]] && continue
			VALS[${KEY}]="${VAL}"
		done < "${PBX_CFG}"
	fi
	VAL='[VxPBX]\n'
	writeConfig 'VAL' 'VALS' "${PBX_CFG}" "${PBX_VERSION}"
}

# Create/Overwrite /etc/vx/VxICS/icsul.conf by keeping unimportant known
# settings and adding/replacing the important stuff.
function updateIcsConfig {
	typeset -A VALS=(
		[DebugLevel]=1 [AppMsgLogging]='ON' [LogToOslog]='false'
		[LogDirectory]="${PBX_LOG_DIR}" [L10nResource]='VxPBX'
		[L10nResourceDir]="${RES_PATH}"
		[L10nLib]="${NB_BASE}/lib/${L10N_LIB}"
		[MaxLogFileSizeKB]=1024 [RolloverMode]='FileSize' [NumberOfLogFiles]=5
		[LogRecycle]='true' [LogFilePermissions]=644
	)
	integer L=${#PBX_ORIG_ID}
	(( L++ ))
	typeset FINAL='' LINE KEY VAL
	if [[ -r ${ICS_CFG} ]]; then
		while read LINE ; do
			if [[ ${LINE:0:$L} == "${PBX_ORIG_ID}." ]]; then
				KEY=${LINE:$L}
				VAL=${KEY#*=}
				KEY=${.sh.match%=}
				[[ -z ${KEY} || -z ${VAL} || ${KEY:0:4} == 'L10n' || \
					-z ${VALS[${KEY}]} ]] && continue
				VALS[${KEY}]="${VAL}"
			else
				FINAL+="${LINE}\n"
			fi	
		done < "${ICS_CFG}"
	fi
	[[ -z ${FINAL} ]] && FINAL='###############################################
# Caution! Do not update/modify file by hand.
# Use vxlogcfg tool to update/modify this file
###############################################\n'
	writeConfig 'FINAL' 'VALS' "${ICS_CFG}" "${NB_VERSION}" ${PBX_ORIG_ID}
}

function updateSwid {
	typeset DEST_FILE="regid.1992-12.com.symantec-netbackup-7.6.0.2_1.swidtag" REG_ID="regid.1992-12.com.symantec" HNAME=${ uname -n ; } \
		SKIP='- skipping SW ID file - not needed.' \
		TMPL="${NB_BASE}/lib/netbackup/${DEST_FILE}.tmpl"

	[[ ! -f ${TMPL} ]] && print -u2 "# ${TMPL} not found ${SKIP}" && return 1
	if [[ ! -d ${TAG_LOCATION}/${REG_ID} ]]; then
		${PRINT} mkdir -p ${TAG_LOCATION}/${REG_ID} || \
			{ print -u2 '# Unable to create ${TAG_LOCATION}/${REG_ID}/ ${SKIP}';
			  return 1 ; }
	fi
	if (( DRY )) ; then
		print "sed -e 's,@NODE_NAME@,${HNAME},' ${TMPL}" \
			">${TAG_LOCATION}/${REG_ID}/${DEST_FILE}"
	else
		sed -e "s,@NODE_NAME@,${HNAME}," ${TMPL} \
			>${TAG_LOCATION}/${REG_ID}/${DEST_FILE}
	fi
}

# Create/Overwrite /etc/vx/vrtslog.conf by keeping unimportant known settings
# and adding/replacing the important stuff.
function updateLogConfig {
	typeset -A VALS=( [Configuration]="${LOG_CFG}" [Names]='VxICS,ics' )
	integer L=${#PBX_PROD_ID}
	(( L++ ))
	typeset FINAL='' LINE KEY VAL
	if [[ -r ${LOG_CFG} ]]; then
		while read LINE ; do
			[[ ${LINE:0:$L} == "${PBX_PROD_ID}." ]] && continue || \
				FINAL+="${LINE}\n"
		done < "${LOG_CFG}"
	fi
	[[ -z ${FINAL} ]] && FINAL='###############################################
# Caution! Do not update/modify file by hand.
# Use vxlogcfg tool to update/modify this file
###############################################\n'
	writeConfig 'FINAL' 'VALS' "${LOG_CFG}" ${PBX_PROD_ID} || return $?
	
	typeset TMPL="${NB_HOME}/nblog.conf.template" \
		F='/etc/netbackup/nblog.conf' OLD="${F}-${NB_VERSION}.pre"
	integer MIGRATE=0
	if [[ ! -e ${OLD} ]]; then
		# we use this as an indicator, whether to run log file migration
		${PRINT} touch ${OLD} || \
			{ print "# Unable to create ${OLD}!"; return 1 ; }
		MIGRATE=1
	fi
	if [[ -f $F ]] && (( MIGRATE )) ; then
		print -u2 "# Replacing $F - a copy gets saved as ${OLD}!"
		${PRINT} cp -p $F ${OLD}	# don't care - no-one customizes it anyway
	fi
	${PRINT} cp -p ${TMPL} $F || \
		{ print -u2 "# Unable to update client logging config!" ; return 2 ; }
	
	# This is required only, if we would actually vxlog binaries here
	typeset -x VRTSLOG_RES_PATH="${RES_PATH}" ICU_DATA="${RES_PATH}"
	${PRINT} ${NB_HOME}/bin/vxlogcfg -a -p ${NB_PROD_ID} -c $F -n NB,nb
	# Not very important - just removes old logs or moves them in a sub dir
	(( MIGRATE )) && ${PRINT} ${NB_HOME}/bin/goodies/migrate_vxul_logs
	return 0
}

function updateClientConfig {
	typeset HNAME=${ uname -n ; } X F TMPL
	typeset -a T=( ${ getent hosts ${HNAME} 2>/dev/null ; } )
	integer I
	for (( I=1 ; I < ${#T[@]} ; I++ )); do
		[[ ${T[$I]} =~ \. ]] && HNAME="${T[$I]}" && break	# FQDN found
	done

	if [[ ! -d /etc/netbackup ]]; then
		${PRINT} mkdir /etc/netbackup || \
			{ print -u2 '# Unable to create local config directory' \
				'/etc/netbackup/ !' ; return 1 ; }
	fi
	F='/etc/netbackup/bp.conf'
	TMPL="${NB_BASE}/lib/netbackup/bp.conf.tmpl"
	if [[ ! -f $F ]]; then
		if [[ -f ${TMPL} ]]; then
			${PRINT} cp -p ${TMPL} $F
		else
			X='SERVER = @SERVER_NAME@\n# ...\nCLIENT_NAME = '
			X+="${HNAME}\nCONNECT_OPTIONS = localhost 1 0 2"
			print -u2 "# ${TMPL} not found."
			(( DRY )) && print "print '$X' >$F" || print "$X" >$F
		fi
		[[ ! -f $F ]] && \
			print -u2 "# Make sure to create a valid ${F}!" || \
			print -u2 '# Please adjust the SERVER_NAME = ... properties' \
				"in your ${F}!"
	fi

	F='/etc/netbackup/include_list'
	[[ ! -f $F ]] && ${PRINT} touch $F

	F='/etc/netbackup/exclude_list'
	TMPL="${NB_BASE}/lib/netbackup/exclude_list.tmpl"
	if [[ ! -f $F ]]; then
		[[ -f ${TMPL} ]] && { ${PRINT} cp -p ${TMPL} $F || ${PRINT} touch $F ; }
	fi
	[[ ! -f $F ]] && print -u2 "# Make sure to have a proper ${F}!"

	F='/etc/netbackup/nbsvcmon.conf'
	TMPL="${NB_BASE}/lib/netbackup/nbsvcmon.conf.tmpl"
	if [[ ! -f $F ]]; then
		[[ -f ${TMPL} ]] && { ${PRINT} cp -p ${TMPL} $F || ${PRINT} touch $F ; }
	fi
	
	typeset -x VRTSLOG_RES_PATH="${RES_PATH}" ICU_DATA="${RES_PATH}"
	${PRINT} ${NB_HOME}/bin/bmrsetupclient
	X="${NB_HOME}/sec/at/bin/vssat setsessioncacheparams -n NBSessionsCache -u on -m 1024 -s 11"
	${PRINT} ${X} || print "# Session caching was not configured (status $?)." \
		'\n# In order to improve overall performance, please execute the' \
		"\n# following command:\n\n\t$X\n"

	for X in bpstart_notify bpend_notify ; do
		if [[ ! -e /etc/netbackup/$X ]]; then
			${PRINT} ln /usr/bin/true /etc/netbackup/$X
			if (( $? )); then
				if (( DRY )); then
					print "print '#!/bin/sh\nexit 0' > /etc/netbackup/$X"
				else
					print '#!/bin/sh\nexit 0' > /etc/netbackup/$X
				fi
			fi
		fi
	done
	return 0
}

function updatePrivatePorts {
	integer P X
	if [[ ! -x /usr/sbin/ipadm ]]; then
		print "# Ports ${PRIV_PORTS[@]} are unprotected against non-root usage!"
		return
	fi
	typeset OLD=${ /usr/sbin/ipadm show-prop -c -o current -p extra_priv_ports tcp 2>/dev/null ; }
	typeset -Ai NODO
	for X in ${OLD//,/ } ; do
		NODO[$X]=1
	done
	for P in ${PRIV_PORTS[@]} ; do
		(( NODO[$P] )) && continue
		${PRINT} /usr/sbin/ipadm set-prop -p extra_priv_ports+=${P} tcp
	done
}

function printUsage {
    getopts -a "${PROG}" "${ print ${USAGE}; }" OPT --man
}

USAGE='[-?$Id: update_config 627 2014-07-10 19:43:56Z elkner $ ]
[-copyright?Copyright (c) 2014 Jens Elkner. All rights reserved.]
[-license?CDDL 1.0]
[+NAME?'"${PROG}"' - [un]]initialize Symantec Private Branch Exchange and Logging]
[+DESCRIPTION?This script can be used to config and initialize the Veritas NetBackup Client (NBC), Logging and Symantec Private Branch Exchange (PBX). Note that in this help text the term "NetBackup client" or short "client" is used to refer to a machine or zone, which needs to be backuped.]
[+?Per default all NetBackup services are disabled. To activate them,
you may use the following commands, depending on your needs:]{
	[+? ]
    [+?svcadm enable netbackup:vnetd]
    [+?svcadm enable netbackup:bpcd]
    [+?svcadm enable netbackup:vxpbx]
	[+?svcadm enable netbackup:nbdisco]
	[+?svcadm enable netbackup:nbftclnt]
	[+?svcadm enable netbackup:bmrbd]
    [+?svcadm enable netbackup:auth]
}
[+?Usually netbackup:\bvnetd\b is sufficient to enable backups of clients and for local text based restore operations (bp, bprestore). You need to open your firewall for incoming traffic on port 13724 (1556 and 13782 not needed).]
[+?Running netbackup:\bbpcd\b (but neither netbackup:vnetd nor netbackup:vxpbx) is sufficient as well for backup and text based restore. In this case you need to open your firewall for incoming traffic on port 13782 (1556 and 13724 not needed than).]
[+?If you decide to run PBX - netbackup:\bvxpbx\b - (required to be able to initiate backup and restores via a remote NetBackup Java console), you also need to enable both netbackup:vnetd and netbackup:bpcd! Otherwise backup and restore will not work. In this case you need to open your firewall for incoming traffic on port 1556 (13724 and 13782 are not needed than). As an alternative, you may block traffic from your backup/media servers on port 1556 (but let them in on 13724) and only allow incoming traffic on 1556 from other hosts running the NetBackup Java console , only. In this case, bpcd can be disabled and backup/restore will still work.]
[+?The netbackup:\bnbdisco\b service manages the NetBackup Discovery daemon - another piece of useless/undocumented stuff hogging about 55 MB RSS of your client'"'"'s memory. If you enable this service and have \bREPORT_CLIENT_DISCOVERIES = NO\b set in you \bbp.conf\b, the daemon gets started and automatically exits after a short time, gets restarted several times by SMF and finally SMF sends the service into maintenance. So poor daemon implementation - disable it.]
[+?The netbackup:\bnbftclnt\b manages the SAN Client Fibre Transport daemon. If you enable this service and have \bSANCLIENT=1\b not set in you \bbp.conf\b, the daemon automatically exits and SMF sends the service into maintenance state.]
[+?The netbackup:\bbmrbd\b service manages the NetBackup Bare Metal Restore daemon - in modern environments another piece of useless stuff. It has the same poor design as the discovery daemon. So if you enable it gets started sucessfully, stops after a certain time if it can'"'"'t contact a BMR server and thus gets re-started serveral times by SMF until it gets sent into maintenance state - disable it.]
[+?The netbackup:\bauth\b service is usually not needed on clients. Its purpose/principle of operation is undocumented and led to security problems in the past - so leave it disabled unless someone tells you to enable it. If disabled remote NetBackup Java console/PBX will use the pam login service of the corresponding client to authenticate users/roles for the desired operations. Otherwise the inetd(1M) will spawn it on demand.]
[+?To check, whether all services are running, use the usual command:]{
	[+?svcs -xv]
}
[+IP FILTER?You might add the following rules to /etc/ipf/ipf.conf at an appropriate place (close to the beginning of your config file):]
{
[+? ]
[+?BACKUP_SERVER="1.2.3.4";]
[+?#BACKUP_SERVER="pool/13724";]
[+?JCONSOLES="pool/1556";]
[+?pass in quick proto tcp from ${BACKUP_SERVER} to port = 13724 flags S keep state]
[+?pass in quick proto tcp from ${JCONSOLES} to port = 1556 flags S keep state]
[+?pass out quick proto tcp to ${BACKUP_SERVER} port = 1556 flags S keep state]
}
[+?The outgoing port is 1556 if the server has PBX running (default), otherwise use 13724 (vnetd). This is sufficient to get listings via bplist, but not for backup and restore, for which you have to open an appropriate port (see above).]
[h:help?Print this help and exit.]
[F:functions?Print a list of all functions available.]
[T:trace]:[functionList?A comma separated list of functions of this script to trace (convinience for troubleshooting).] 
[n:dry?Just show, what would be done, but do not actually do it.]
[i:install?Initialize PBX, Logging and the client.]'

integer DRY=0 INSTALL=0
while getopts -a "${PROG}" "${ print ${USAGE}; }" OPT ; do
    case "${OPT}" in
        h) printUsage ; exit 0 ;;
        T) typeset -ft ${OPTARG//,/ } ;;
        F) typeset +f && exit 0 ;;
		n) DRY=1 ;;
		i) INSTALL=1 ;;
    esac
done
integer IDX=$((OPTIND-1))
shift $IDX

(( DRY )) && PRINT='print' || PRINT=''

if (( INSTALL == 1 )); then
	# updateLogConfig needs to be the last since it uses the vxlog* binaries -
	# the others use a 'text based bootstrapping' to ensure that it'll work
	updateClientConfig && updatePbxConfig && updateIcsConfig && updateLogConfig
	(( $? )) && print -u2 '\n# Setup incomplete - exiting!\n' && exit 1
	# Save the triple
	X=( ${NB_VERSION//./ } )
	X+=( 0 0 0 )
	Y="${X[@]:0:3}"
	Y="setprop config/version = ${Y// /.}\nrefresh"
	(( DRY )) && print "print '$Y' | ${SVCCFG} -s ${SVC}" || \
		print "$Y" | ${SVCCFG} -s ${SVC}
	updatePrivatePorts
	updateSwid
else
	printUsage
fi
