#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#

# =============================================================================
# =============================================================================
# ai_publish_pkg
#
# Publish the package image area into an pkg(5) repository.
# =============================================================================
# =============================================================================


# Set up the builtin commands.
builtin cat
builtin print
builtin rm
builtin uname

#
# Establish PATH for non-built in commands
#
export PATH=/usr/xpg4/bin:/bin:/usr/bin:/usr/sbin:/usr/lib

# Define non-core-OS commands.
MANIFEST_READ=/usr/bin/ManifestRead

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# usage
#       Print the usage message.
#
#       This program runs as a Distro Constructor finalizer script.
#       The first five arguments are provided directly by the
#       Distro Constructor.
#       Any remaining arguments need to be specifed in the Distro
#       Constructor manifest.
#
# Input: none
# Returns: none
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function usage
{
        print -u2 "\n\tUsage: "
        print -u2 "\t${PROG} runs as a Distro Constructor finalizer script."
        print -u2 "\tThe first five arguments are provided directly by"
        print -u2 "\tthe Distro Constructor."
        print -u2 "\tAny remaining arguments need to be specifed in "
        print -u2 "\tthe Distro Constructor manifest. \n"
        print -u2 "\tThe first five arguments passed by Disto Construtor are:"
        print -u2 "\t\treader socket, pkg_image area, temporary directory,"
        print -u2 "\t\tbootroot build area, media area \n"
        print -u2 "\tThe remaining optoinal arguments, as specified in the"
        print -u2 "\tDistro Constructor manifest, are:"
        print -u2 "\t\tpackage name"
        print -u2 "\t\tpackage repository URL (protocol file:)"
        print -u2 "\t\tpublisher prefix (no underscores)"
}


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# set_locale
#
#	Make sure all math stuff runs in the "C" locale to avoid problems
#	with alternative radix point representations (e.g. ',' instead of
#	'.' in de_DE.*-locales). This needs to be set _before_ any
#	floating-point constants are defined in this script).
#
# Input: none
# Returns: none
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function set_locale
{
        if [[ "${LC_ALL}" != "" ]] ; then
                export \
                LC_MONETARY="${LC_ALL}" \
                LC_MESSAGES="${LC_ALL}" \
                LC_COLLATE="${LC_ALL}" \
                LC_CTYPE="${LC_ALL}"
                unset LC_ALL
        fi
        export LC_NUMERIC=C

}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# cleanup
#
#	This function attempts to clean up any resources this script
#	could generate. Depending on where in the script this function
#	is involved some resources may not be there to cleanup, but
#	that will not adversely effect anything.
#
#	This function is not defined using the function keyword
#	to avoid an exit loop.
#
# Input: none
# Returns: none
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cleanup ()
{
	#
	# It is not necessary to process errors.
	#
	{

		trap "" ERR INT
		set +o errexit

		#
		# Cleanup any repository possibly created by this script.
		#

		#
		# Close it if it was opened. PKG_TRANS_ID and PKG_REPO
		# will be set if it was opened.
		# 
		#
		if [[ ! -z ${PKG_TRANS_ID} ]]; then
			pkgsend -s ${PKG_REPO} close
		fi

		# The pkg_repo_path will be empty if the user had created
		# it, started a depo server and specified http protocol for
		# the optional argument: pkg_repo.
		#
		if [[ ! -z ${pkg_repo_path} ]]; then
			rm -rf ${pkg_repo_path} > /dev/null 2>&1
		fi


	} > /dev/null 2>&1
}


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# error_handler and error_handler_trap
#
#	The error_handler will set up the traps,
#	invoke cleanup to clean up any temporary resources then
#	exit with an error or 1.
#
#	These function is not defined using the function keyword
#	to avoid an exit loop.
#
#	error_handler_trap is the entry point registered with
#	the trap command for interupts (INT). Using this extra
#	level allows arguments to be passed to error_handler().
#
# Input:
#	stat - error status code to act upon
#       msg  - message to issue if an error_code is not success
#
# Returns: none
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error_handler_trap ()
{
	print -u2 "\nInvoking: error_handler_trap"
	error_handler 1 "Interrupt encountered. Exiting"
}

error_handler ()
{
	typeset -i stat=$1
	typeset    msg="$2"

	if [[ ${stat} != 0 ]] ; then
		print -u2 "\nInvoking: error_handler"

		trap "" ERR INT
		set +o errexit

		print -u2 "${msg}"

		#
		# cleanup must be defined in the caller.
		#
		cleanup

		exit 1
	fi
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Main
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Publish the package image area into an pkg(5) repository.
# 
# Args:
#   MFEST_SOCKET: Socket needed to get manifest data via ManifestRead object
#
#   PKG_IMG_PATH: Package image area
#
#   TMP_DIR: Temporary directory to contain the bootroot file
#
#   BA_BUILD: Area where boot archive is put together.
#
#   MEDIA_DIR: Area where the media is put.
# 
#   PKG_NAME_SUPPLIED: The package name
#
#   PKG_REPO_SUPPLIED: The package repository URL, protocol must befile.
#
#   PUB_PREF_SUPPLIED: The publisher prefix, no special characters
#                      including no underscores.
#
# Note: This assumes a populated pkg_image area exists at the location
#		${PKG_IMG_PATH} and that the bootroot has been built.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#
# Make sure all math stuff runs in the "C" locale
#
set_locale

#
# Process command line arguments
#
typeset -r PROG="$0"
if [[ ($# < 5) || ($# > 8) ]]; then
        print -u2 "\t${PROG}: Requires a minimum of 5 and a maximum" \
	    " of 8 arguments. $# supplied."
	usage
	exit 1
fi

#
# Validate the arguments
#
typeset invalid_argument_found=false

#
# Process the 5 required arguments
#
typeset -r MFEST_SOCKET="$1"
typeset -r PKG_IMG_PATH="$2"
if [[ ! -d ${PKG_IMG_PATH} ]] ; then
	print -u2 "\t${PROG}: Unable to access pkg_image area $PKG_IMG_PATH"
	invalid_argument_found=true
fi

typeset -r TMP_DIR="$3" 
if [[ ! -d $TMP_DIR ]] ; then
	print -u2 "\t${PROG}: Unable to access temporary directory $TMP_DIR"
	invalid_argument_found=true
fi

typeset -r BA_BUILD="$4" 
if [[ ! -d $BA_BUILD ]] ; then
	print -u2 "\t${PROG}: Unable to access bootroot directory $BA_BUILD"
	invalid_argument_found=true
fi

typeset -r MEDIA_DIR="$5"
if [[ ! -d $MEDIA_DIR ]] ; then
	print -u2 "\t${PROG}: Unable to access media directory $MEDIA_DIR"
	invalid_argument_found=true
fi      

#
# Process any optional arguments
# 
typeset opt_arg_supplied=""
typeset opt_arg_key=""
typeset PKG_NAME_SUPPLIED=""
typeset PKG_REPO_SUPPLIED=""
typeset PUB_PREF_SUPPLIED=""
shift 5
while (( $# > 0)); do
	opt_arg_supplied="$1"
	opt_arg_key=${opt_arg_supplied%=*}
	
	case "${opt_arg_key}" in

		pkg_name)
			PKG_NAME_SUPPLIED=${opt_arg_supplied#*=}
		;;

		pkg_repo)
			PKG_REPO_SUPPLIED=${opt_arg_supplied#*=}
		;;

		publisher_prefix)
			PUB_PREF_SUPPLIED=${opt_arg_supplied#*=}
		;;

		*)
			print -u2 "\t${PROG}: Invalid argument specifying" \
			    "${opt_arg_supplied}"
			invalid_argument_found=true
		;;

	esac
	
	
        shift
done

if $invalid_argument_found ; then
        print -u2 "\n\tError encountered: Invalid argument found. Exiting"
        usage
        exit 1
fi

#
# Local command variables
#
typeset -i cmdsts=0
typeset    cmdout=""

#
# Read the distribution name tag from the manifest 
#
typeset -r DIST_NAME=$(${MANIFEST_READ} ${MFEST_SOCKET} "name")

#
# Set the default package name to be the distribution_name from
# the manifest appended with the version of the auto-install
#  package installed in the package image area.
# The version string for the auto-install package is stored
# by DC checkpoint ai-im-mod in file: ${TMP_DIR}/ai_pkg_version.
#      e.g.: image/automated_installer_image@5.11-0.134

typeset -r AI_PKG_VER_FILE="${TMP_DIR}/ai_pkg_version"
cmdout=$(cat ${AI_PKG_VER_FILE} 2>&1)
cmdsts=$?
error_handler ${cmdsts} "\nAccessing ${AI_PKG_VER_FILE} failed with ${cmdout}"
typeset -r PKG_NAME_DEFAULT="image/${DIST_NAME}@${cmdout}"

#
# If available use the supplied pkg name. If not suppled
# use the default name.
#
typeset -r PKG_NAME=${PKG_NAME_SUPPLIED:-${PKG_NAME_DEFAULT}}

#
# Set: Destination pkg(5) Repository Publisher Prefix
#      Defaults to "ai-image if not supplied.
#
typeset -r PUB_PREF=${PUB_PREF_SUPPLIED:-"ai-image"}

#
# Set: Destination pkg(5) Repository Name
#      Defaults to "file://${MEDIA_DIR}/ai_image_repo if not supplied.
#
typeset -r PKG_REPO=${PKG_REPO_SUPPLIED:-"file://${MEDIA_DIR}/ai_image_repo"}
typeset    pkg_repo_path=""

#
# Set up error handling.
#
trap "error_handler_trap" INT
set +o errexit

if [[ ${PKG_REPO} == ~(E)file: ]]; then
	pkg_repo_path=${PKG_REPO//file:\/\//}

	#
	# Create the repository if the specified protocol is: "file:".
	#
	pkgsend -s ${PKG_REPO} create-repository --set-property \
	    publisher.prefix=${PUB_PREF}
	cmdsts=$?
	error_handler ${cmdsts} "\ncreate repository failed"
fi

#
# Open the repository
#
#     pkgsend open writes to standard output a command of the form:
#     export PKG_TRANS_ID="%s"
#
#     This command needs to be executed when pkgsend open succeeds
#     so consecutive commands will be able to access the value of
#     PKG_TRANS_ID.
#
#     Upon failure the output will be logged.
#
cmdout=$(pkgsend -s ${PKG_REPO} open ${PKG_NAME} 2>&1)
cmdsts=$?
error_handler ${cmdsts} "\npkgsend open failed with: ${cmdout}"
${cmdout}

#
# Import the image into the repository
#
pkgsend -s ${PKG_REPO} import ${PKG_IMG_PATH}
cmdsts=$?
error_handler ${cmdsts} "\npkgsend import failed"

#
# Close/abandon the current transaction/repository
#
pkgsend -s ${PKG_REPO} close
cmdsts=$?
error_handler ${cmdsts} "\npkgsend close failed"

#
# exit with success status
#
exit 0

