#!/bin/ksh

################################################################################
#
#	Copyright (c) 1999-2003 by Sun Microsystems, Inc. All Rights Reserved.
#
# ident "@(#)ludesc.sh 1.5     03/02/04 SMI"
#
# PUBLIC USAGE:	ludesc ( -A beDesc | -n beName [ -f ( - | file ) | beDesc ] | 
#			-f ( - | file ) )
#
# PRIVATE USAGE: ludesc -d beId
#
# FUNCTION:	Inquire or modify boot environment description.
#
# Note: performance testing has proven that "/sbin/sh" provides the best 
# performance over /bin/sh and /bin/ksh.
#
################################################################################

# disable error on unset variable reference in case set in global environment
set +o nounset

LU_PROG_FULL_PATH="$0"
LU_PROG_NAME="`basename ${LU_PROG_FULL_PATH}`"; export LU_PROG_NAME

################################################################################
# Name:		usage
# Description:	output command line usage information; then call exit_script 
#		to terminate execution.
# Local Prefix:	<none>
# Arguments:	$1 = exit code for script ("" defaults to "3").
# Example:	usage 3
# Returns:	<none> 
################################################################################

usage()
{
  ${LUPRINTF} -p2 "`gettext 'USAGE: %s [ -X ] [ -l error_log ] [ -o outfile ] \
( -A BE_description | -n BE_name \
[ -f ( - | BE_description_file ) | BE_description ] \
| -f ( - | BE_description_file ) )'`" \
"${LU_PROG_NAME}"
  ${LUPRINTF} -Ip2 "`gettext 'Any BE_name or BE_description should be enclosed \
in single quotes.'`"
  if [ -z "$1" ] ; then
    exit_script 3 
  fi
  exit_script "$1"
}

################################################################################
# Name:		exit_script
# Description:	Perform cleanup operations and exit this script.
# Local Prefix:	<none>
# Arguments:	$1 = optional exit value for script ("" or none is = "0")
# Example:      exit_script "0"
# Returns:	<none>
################################################################################

exit_script()
{
  # Determine the exit status code.

  retcode="0"
  if [ -n "$1" ] ; then
    retcode="$1"
  fi

  exit "${retcode}"
}

################################################################################
# Name:		propagate_lu_db_global
# Description:	Propagate the global BE database to all BEs.
# Local Prefix:	<none>
# Arguments:	<none>
# Example:      propagate_lu_db_global
# Returns:	<none>
################################################################################

propagate_lu_db_global()
{
	${LUPRINTF} -lp1 "`gettext 'Updating boot environment description database on all BEs.'`"
	if [ -f "${LU_DB_GLOBAL}" ] ; then
		${LUBIN}/luupdall "${LU_DB_GLOBAL}"
		if [ $? != 0 ] ; then
			${LUPRINTF} -Welp2 "`gettext 'Unable to propagate the files \
	<%s> to all BEs.'`" "${LU_DB_GLOBAL}"
		fi
	fi
}

################################################################################
# Name:		remove_be_description
# Description:	Remove the description for a be given the be's id.
# Local Prefix:	rbd
# Arguments:	$1 = database file to remove description from.
#		$2 = id of BE to remove description for.
# Example:      remove_be_description "${LU_GLOBAL_DB}" "${beId}"
# Returns:	<none>
################################################################################

remove_be_description()
{
	rbd_dbFile="$1"
	rbd_beId="$2"
	rbd_tmpFile="/tmp/ludesc.rbd.$$"

	${LUPRINTF} -lp2D - "`gettext 'Remove boot environment description for id <%d>.'`" \
"${rbd_beId}"

	if [ -s "${rbd_dbFile}" ] ; then
		/bin/fgrep -v "<beDescription beId=\"${rbd_beId}\" " \
< ${rbd_dbFile} > ${rbd_tmpFile}
		/bin/mv -f ${rbd_tmpFile} ${rbd_dbFile}
	fi

	return 0
}

################################################################################
# Name:		add_be_description
# Description:	Add the description for a be given the be's id.
# Local Prefix:	abd
# Arguments:	$1 = database file to add description to.
#		$2 = id of BE to add description for (MUST BE XML ENCODED!).
#		$3 = be description to add.
# Example:      add_be_description "${LU_GLOBAL_DB}" "${beId}" "${beDesc}"
# Returns:	<none>
################################################################################

add_be_description()
{
	abd_dbFile="$1"
	abd_beId="$2"
	abd_beDesc="$3"

	${LUPRINTF} -lp2D - "`gettext 'Set boot environment description for ID <%d>.'`" \
"${abd_beId}"

	echo "${abd_beDesc}" | ${LUPRINTF} +X -a "${abd_dbFile}" \
'<beDescription beId="%d" encodedDescription="%R" />' "${abd_beId}"

	return $?
}

################################################################################
# Name:		delete_be_desc_from_id
# Description:	Given a BE id, delete the description for that BE.
# Local Prefix:	dbd
# Arguments:	$1 = BE id of BE to delete description of.
# Example:      delete_be_desc_from_id "${BE_ID}"
# Returns:	0 - success, 1 - failure
################################################################################

delete_be_desc_from_id()
{
	dbd_beId="$1"

	remove_be_description "${LU_DB_GLOBAL}" "${dbd_beId}"
	dbd_ret="$?"

	# Update databases on all other BEs.
	propagate_lu_db_global

	return ${dbd_ret}
}

################################################################################
# Name:		get_be_name_from_file
# Description:	Given a file containing a BE description, return the BE name.
# Local Prefix:	gbn
# Arguments:	$1 = file name containing description to search for.
# Example:      get_be_name_from_file "${file}"
# Returns:	0 - success, 1 - failure
################################################################################

get_be_name_from_file()
{
	gbn_fileName="$1"

	${LUPRINTF} -lp2D - "`gettext 'Get boot environment name from file <%s>.'`" \
"${gbn_fileName}"

	gbn_beName="`${LUETCBIN}/ludo get_be_name_from_desc_file \"${gbn_fileName}\"`"

	if [ "$?" -ne '0' ] ; then
		[ -n "${gbn_beName}" ] && ${LUPRINTF} -Eelp2 '%s' "${gbn_beName}"
		${LUPRINTF} -Eelp2 "`gettext 'Unable to determine boot \
environment name from description provided.'`"	
		return 1
	fi

	# If XML output requested, output be status, else output be name.
	if [ -n "${_LU_OUTPUT_XML_}" ] ; then
		__LU_SUPPRESS_XML_GENERAL_MESSAGES__="yes" \
		${LUBIN}/lustatus -X "${gbn_beName}"
	else
		${LUPRINTF} -1 '%s' "${gbn_beName}"
	fi
	return 0
}

################################################################################
# Name:		get_be_name_from_desc
# Description:	Given a BE description, return the BE name.
# Local Prefix:	gbn
# Arguments:	$1 = BE description to search for.
# Example:      get_be_name_from_desc "beDesc"
# Returns:	0 - success, 1 - failure
################################################################################

get_be_name_from_desc()
{
	gbn_beDesc="$1"

	${LUPRINTF} -lp2D - "`gettext 'Get boot environment name for description.'`"

	gbn_beName="`${LUPRINTF} -1n '%s' \"${gbn_beDesc}\" \
| ${LUETCBIN}/ludo get_be_name_from_desc_file -`"

	if [ "$?" -ne '0' ] ; then
		[ -n "${gbn_beName}" ] && ${LUPRINTF} -Eelp2 '%s' "${gbn_beName}"
		${LUPRINTF} -Eelp2 "`gettext 'Unable to determine boot \
environment name from description provided.'`"	
		return 1
	fi

	${LUPRINTF} -lp2D - "`gettext 'Boot environment id <%d> name <%s>.'`" \
"${gbn_beId}" "${gbn_beName}"

	# If XML output requested, output be status, else output be name.
	if [ -n "${_LU_OUTPUT_XML_}" ] ; then
		__LU_SUPPRESS_XML_GENERAL_MESSAGES__="yes" \
		${LUBIN}/lustatus -X "${gbn_beName}"
	else
		${LUPRINTF} -1 '%s' "${gbn_beName}"
	fi
	return 0

}

################################################################################
# Name:		get_be_desc
# Description:	Given a BE name and a be ID, return the BE description.
# Local Prefix:	gbd
# Arguments:	$1 = BE name to return description for.
#		$2 = BE id to return description for.
# Example:      get_be_desc "beName"
# Returns:	0 - success, 1 - failure
################################################################################

get_be_desc()
{
	gbd_beName="$1"
	gbd_beId="$2"

	${LUPRINTF} -lp2D - "`gettext 'Get boot environment desc for <%s> id <%s>.'`" \
"${gbd_beName}" "${gbd_beId}"

	# If no database, no description for BE.
	if [ ! -s "${LU_DB_GLOBAL}" ] ; then
		return 0
	fi

	# If XML output requested, output raw data, else output decoded
	# BE description.
	if [ -n "${_LU_OUTPUT_XML_}" ] ; then
		/bin/grep '^<beDescription ' < "${LU_DB_GLOBAL}" \
		| /bin/fgrep " beId=\"${gbd_beId}\" "
	else
		/bin/grep '^<beDescription ' < "${LU_DB_GLOBAL}" \
		| /bin/fgrep " beId=\"${gbd_beId}\" " \
		| /bin/cut -d'"' -f 4 \
		| /bin/tr -d '\n' \
		| ${LUETCBIN}/ludo xml_decode_string
	fi

	return 0
}

################################################################################
# Name:		change_be_desc_from_file
# Description:	Given a BE name, a be ID, and a file containing a description,
#		update the description for the specified BE.
# Local Prefix:	cbd
# Arguments:	$1 = BE name to set description for.
#		$2 = BE id to set description for.
#		$3 = name of file containing the description to set.
# Example:      change_be_desc_from_file "beName" "beId" "fileName"
# Returns:	0 - success, 1 - failure
################################################################################

change_be_desc_from_file()
{
	cbd_beName="$1"
	cbd_beId="$2"
	cbd_fileName="$3"

	${LUPRINTF} -lp2D - "`gettext 'Change boot environment description for <%s> id <%s> \
from file <%s>.'`" "${cbd_beName}" "${cbd_beId}" "${cbd_fileName}"

	if [ "${cbd_fileName}" = '-' ] ; then
		cbd_beDesc="`${LUETCBIN}/ludo xml_encode_string`"
	else
		cbd_beDesc="`${LUETCBIN}/ludo xml_encode_string < ${cbd_fileName}`"
	fi

	change_be_desc "${cbd_beName}" "${cbd_beId}" "${cbd_beDesc}"
	return "$?"
}

################################################################################
# Name:		change_be_desc
# Description:	Given a BE name, a be ID, and a BE description,
#		update the description for the specified BE.
# Local Prefix:	cbd
# Arguments:	$1 = BE name to set description for.
#		$2 = BE id to set description for.
#		$3 = the description to set (MUST BE XML ENCODED!).
# Example:      change_be_desc "beName" "beId" "description"
# Returns:	0 - success, 1 - failure
################################################################################

change_be_desc()
{
	cbd_beName="$1"
	cbd_beId="$2"
	cbd_beDesc="$3"

	echo "${cbd_beDesc}" | ${LUPRINTF} -lp2D - "`gettext 'Change boot environment \
description for <%s> id <%s> to <%R>.'`" "${cbd_beName}" "${cbd_beId}" 

	# If a non-empty description is to be set, see if a boot environment
	# already has the requested description; if so generate an error.
	if [ -n "${cbd_beDesc}" ] ; then
		cbd_xbeName="`get_be_name_from_desc \"${cbd_beDesc}\" 2>/dev/null`"
		if [ "$?" -eq '0' -a -n "${cbd_xbeName}" ] ; then
			[ "${cbd_xbeName}" = "${cbd_beName}" ] && return 0
			${LUPRINTF} -Eelp2 "`gettext 'The description is already in \
use by boot environment <%s>.'`" "${cbd_xbeName}"
			return 1
		fi
	fi

	# Indicate operation to be performed.
	if [ -n "${cbd_beDesc}" ] ; then
		${LUPRINTF} -1lp "`gettext 'Setting description for boot environment <%s>.'`" \
"${cbd_beName}"
	else
		${LUPRINTF} -1lp "`gettext 'Removing description for boot environment <%s>.'`" \
"${cbd_beName}"
	fi

	# Do the work.
	remove_be_description "${LU_DB_GLOBAL}" "${cbd_beId}"
	add_be_description "${LU_DB_GLOBAL}" "${cbd_beId}" "${cbd_beDesc}"

	# Update databases on all other BEs.
	propagate_lu_db_global

	return 0
}

################################################################################
# Name:		<main>
# Description:	Main code (outside of any function definitions) - 
#		executed at script startup.
# Local Prefix:	<none>
# Arguments:	$0...$n = All arguments specified by user on command line that 
#		invoked this script.
################################################################################

# Dot the defaults file.

if [ ! -s /etc/default/lu ] ; then
  echo "${LU_PROG_NAME}: ""`gettext 'ERROR: Live Upgrade not installed \
properly (/etc/default/lu not found).'`"
  exit 1
fi
. /etc/default/lu

LUBIN=${LUBIN:=/usr/lib/lu}
### LU_OPTFS=${LU_OPTFS:=/etc/lu/optfs}
COPYLOCK=${COPYLOCK:=/etc/lu/COPY_LOCK}
NEXT_ACTIVE=${NEXT_ACTIVE:=/etc/lu/.NEXT_ACTIVE}

# Dot the Live Upgrade library functions.

if [ ! -s $LUBIN/lulib ] ; then
  echo "${LU_PROG_NAME}: ""`gettext 'ERROR: The Live Upgrade product is not \
installed properly (${LUBIN}/lulib not found).'`"
  exit 1
fi

. $LUBIN/lulib

# Check for existence and non-zero size of lutab file.

if [ ! -f /etc/lutab -o ! -s /etc/lutab ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'No BEs are configured on this system.'`"
  exit 1
fi

  ############################################################################
  ################ Command line option and argument processing ###############
  ############################################################################

# Reset all command line parse flags to default values.
flag_A="" # -A "desc" - prints the name for be with description 'desc'.
flag_d="" # -d "beId" - delete description for BE specified by id (PRIVATE).
flag_f="" # -f "file|-" - file to obtain BE description from.
flag_l="" # -l f - log file path.
flag_n="" # -n "beName" - name of BE to operate on.
flag_N="" # -N - be desc to set is XML ready / dont validate copylock (PRIVATE).
flag_o="" # -o f - output file path.
flag_x="" # -x n = set debug level to n (PRIVATE).
beDesc=""

while [ $# -ne 0 ] ; do
  while getopts A:d:f:l:n:No:x:X c
  do
    case $c in
      A) # -A "desc" - prints the name for be with description 'desc'.
	 if [ -n "${flag_A}" ] ; then
	   ${LUPRINTF} -Eelp2 "`gettext 'The <-A> option may not be specified \
more than once.'`"
	   usage 3
	 fi
	 beDesc="`${LUETCBIN}/ludo xml_encode_string \"${OPTARG}\"`"
	 flag_A='yes'
	 ;;
      d) # -d "beId" - delete description for BE specified by id (PRIVATE).
	 lulib_cannot_duplicate_option "${flag_d}" "${OPTARG}" "-d"
	 flag_d="${OPTARG}"
	 ;;
      f) # -f "file|-" - file to obtain BE description from.
	 lulib_cannot_duplicate_option "${flag_f}" "${OPTARG}" "-f"
	 if [ "${OPTARG}" != '-' -a ! -s "${OPTARG}" ] ; then
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to option <-f> must be a \
non-empty file containing the boot environment description to use.'`" "${OPTARG}"
	   usage 3
	 fi
	 flag_f="${OPTARG}"
	 ;;
      l) # -l f - error log file path.
	 # This overrides the LU_ERROR_LOG_FILE setting read from /etc/default/lu
	 lulib_cannot_duplicate_option "${flag_l}" "${OPTARG}" "-l"
	 ${LUPRINTF} -lp2D - "`gettext 'Verifying that the error log file <%s> \
specified can be created and appended to.'`" "${OPTARG}"
	 ERRMSG="`${LUPRINTF} -c \"${OPTARG}\" 2>&1`"
	 if [ $? -ne 0 ] ; then
	   [ -n "${ERRMSG}" ] && ${LUPRINTF} -elp2 '%s' "${ERRMSG}"
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to -l option may not be \
created or appended to.'`" "${OPTARG}"
	   exit_script 3
	 fi
	 flag_l="${OPTARG}"
	 lulib_set_error_log_file "${flag_l}"
	 ;;
      n) # -n "beName" - name of BE to operate on.
	 lulib_cannot_duplicate_option "${flag_n}" "${OPTARG}" "-n"
	 flag_n="${OPTARG}"
	 ;;
      N) # -N - be desc to set is XML ready / dont validate copylock (PRIVATE).
         flag_N='yes'
	 ;;
      o) # -o f - output file path.
	 # This overrides the LU_SESSION_LOG_FILE setting read from /etc/default/lu
	 lulib_cannot_duplicate_option "${flag_o}" "${OPTARG}" "-o"
	 ${LUPRINTF} -lp2D -  "`gettext 'Verifying that the session log file \
<%s> can be created and appended to.'`" "${OPTARG}"
	 ERRMSG="`${LUPRINTF} -c \"${OPTARG}\" 2>&1`"
	 if [ $? -ne 0 ] ; then
	   [ -n "${ERRMSG}" ] && ${LUPRINTF} -elp2 '%s' "${ERRMSG}"
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to -o option may not be \
created or appended to.'`" "${OPTARG}"
	   exit_script 3
	 fi
	 flag_o="${OPTARG}"
	 lulib_set_session_log_file "${flag_o}"
	 ;;
      x) # -x n - set debug level to n (PRIVATE).
	 # This overrides the default setting read from /etc/default/lu
	 lulib_cannot_duplicate_option "${flag_x}" "${OPTARG}" "-x"
	 /bin/test "${OPTARG}" -ge 0 2>/dev/null
	 if [ $? -gt 1 ] ; then
	   ${LUPRINTF} -Eelp2 "`gettext 'Argument <%s> to -x option is not a \
number.'`" "${OPTARG}"
	   usage 3
	 fi
	 flag_x="${OPTARG}"
	 lulib_set_debug "${flag_x}"
	 ;;
      X) # -X - set XML output mode.
	  lulib_set_output_format 'xml'
	  ;;
      \?) # unknown - option.
	  usage 3
	  esac
  done

  # Found either end of arguments, +option, or non-option argument; shift out
  # what has been processed so far; if a non-option argument is present
  # capture it and continue processing the command line arguments.
  shift `/bin/expr $OPTIND - 1`
  OPTIND=1
  if [ $# -ne 0 -a "$1" = '+X' ] ; then
      # +X - set TEXT output mode.
      lulib_set_output_format 'text'
      shift
  else
    break
  fi
done

# Fixup debug, session log, and error log settings
lulib_fixup_startup_settings

  ######################################################################################
  ############ Validate all command line arguments and options as possible #############
  ######################################################################################

# If command line arguments are given, convert them to an encoded BE description.
if [ "$#" -ne "0" ] ; then
  if [ -z "${flag_N}" ] ; then
    be_Desc="`${LUETCBIN}/ludo xml_encode_string \"$*\"`"
  else
    be_Desc="$*"
  fi
fi

#
# Validate command line arguments.
#

# At least one of the -A, -f or -n option s must be specified.
if [ -z "${flag_A}" -a -z "${flag_n}" -a -z "${flag_f}" -a -z "${flag_d}" ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'At least one option from <-A, -f, -n> must be \
specified.'`"
  usage 3
fi

# Check for any conflicting options.
lulib_cannot_have_conflicting_option "${flag_A}" "-A" "${flag_n}" "-n"
lulib_cannot_have_conflicting_option "${flag_A}" "-A" "${flag_f}" "-f"
lulib_cannot_have_conflicting_options "${flag_A}${flag_n}${flag_f}" "-A, -n, -f" "${flag_d}" "-d"

# -A may not be combined with a boot environment description.
if [ -n "${flag_A}" -a -n "${be_Desc}" ] ; then
    ${LUPRINTF} -Eelp2 "`gettext 'The <-A> option may not be combined \
with a boot environment description.'`"
    usage 3
fi

# The boot environment description may not be combined with -f.
if [ -n "${be_Desc}" -a -n "${flag_f}" ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'The boot environment description may not be \
combined with the <-f> option.'`"
  usage 3
fi

# Validate -n option: If a BE name is provided, it must be valid.
if [ -n "${flag_n}" ] ; then
  lulib_is_be_name_inuse "${flag_n}"
  [ "$?" -ne "0" ] && exit_script 2
  be_Id="`${LUETCBIN}/ludo get_be_id \"${flag_n}\" 2>&1`"
  if [ "$?" -ne 0 -o -z "${be_Id}" ] ; then
    [ -n "${be_Id}" ] && ${LUPRINTF} -Eelp2 "%s" "${be_Id}"
    exit_script 1
  fi
fi

# If this operation will modify any data, make sure there is no copylock.
# Do not verify copylock if -N (private) option specified.
if [ -z "${flag_N}" -a '(' -n "${flag_f}" -o -n "${be_Desc}" ')' ] ; then
  lulib_validate_lulock
  if [ -f $COPYLOCK ] ; then
    . $COPYLOCK
    ${LUPRINTF} -lp2D - "`gettext 'A Copy Lock file <%s> exists: Status <%s> \
Source BE <%s> Target BE <%s>.'`" "${COPYLOCK}" "${CL_STATUS}" \
"${CL_SOURCE_BE}" "${CL_TARGET_BE}"
    ${LUPRINTF} -Eelp2 "`gettext 'Another Live Upgrade process is either \
running or scheduled...cannot update the boot environment description.'`"
    exit_script 2
  fi
fi

# 
# Validations completed; perform primary function.
#
if [ -n "${flag_d}" ] ; then
  delete_be_desc_from_id "${flag_d}"
elif [ -n "${flag_A}" -a -z "${flag_n}" ] ; then
  get_be_name_from_desc "${beDesc}"
elif [ -n "${flag_n}" -a -z "${flag_A}" ] ; then
  if [ -z "${flag_f}" -a -z "${be_Desc}" ] ; then
    get_be_desc "${flag_n}" "${be_Id}"
  elif [ -n "${flag_f}" -a -z "${be_Desc}" ] ; then
    change_be_desc_from_file "${flag_n}" "${be_Id}" "${flag_f}"
  elif [ -z "${flag_f}" -a -n "${be_Desc}" ] ; then
    change_be_desc "${flag_n}" "${be_Id}" "${be_Desc}"
  fi
elif [ -n "${flag_f}" -a -z "${flag_A}" -a -z "${flag_n}" ] ; then
  get_be_name_from_file "${flag_f}"
else
  usage 3
fi

exit_script "$?"
