#!/sbin/sh
#
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# ident	"@(#)luclonefs.sh	1.6	09/07/29 SMI"
#
# This utility is not for public use; the interface is subject to change 
# without notice.
#
# USAGE:        luclonefs -a <ABE ICF> -p <PBE ICF>>
# FUNCTION:     clones PBE file systems according to ABE & PBE ICF
# INPUT:        ABE ICF & PBE ICF
# OUTPUT:
#
# ALGORITHM:
# 1. Determine all of the non shared and swap file systems
# 2. Snapshot/clone each of the file systems
#
###############################################################################
#

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

#################################################################################################
# Name:		interruptHandler
# Description:	Handle an armed shell "trap"
# Local Prefix:	<none>
# Arguments:	<none>
# Example:      trap "interruptHandler" 1 2 3 9 15
# Returns:	call script cleanup function with exit code "4"
#################################################################################################

interruptHandler()
{
  # Reset all traps to be ignored so that termination can take place
  # without further interrupts.

  # 1- SIGHUP (hangup).
  # 2- SIGINT (user interrupt).
  # 3- SIGQUIT (user quit).
  # 9- SIGKILL (kill process - can not be caught or ignored :).
  # 15- SIGTERM (software termination request).
  trap "" 1 2 3 9 15

  # Output indication of interrupt processed.
  ${LUPRINTF} -Ilp2 "`gettext 'Interrupted (Signal received): cleaning up...'`"

  # Cause the script to exit.
  exit_script 1
}

#################################################################################################
# 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 [-p mountpt] [-s skip-file] -i <internal_config_file>'`" "${LU_PROG_NAME}"
  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:		<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}

# 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 n - abe icf file to use.
flag_p="" # -p n - pbe icf file to use.
flag_i="" # internal flag

while getopts a:p:i c
do
    case $c in
      a) # -a abe_icf_file
	 lulib_cannot_duplicate_option "${flag_a}" "${OPTARG}" "-a"
	 flag_a="${OPTARG}"
	 ABE_ICF="${OPTARG}"
	 ;;

      p) # -p pbe_icf_file
	 lulib_cannot_duplicate_option "${flag_p}" "${OPTARG}" "-p"
	 flag_p="${OPTARG}"
	 PBE_ICF="${OPTARG}"
	 ;;
      i) # -i internal flag
	 flag_i="yes"
	 ;;

      \?) # unknown - option.
	  usage 3
    esac
done
shift `/bin/expr $OPTIND - 1`

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

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

# If any command line arguments provided, exit with error.
if [ "$#" -ne "0" ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'Command line arguments <%s> not allowed.'`" "$*"
  usage 3
fi

# Make sure that a ABE internal configuration file name was specified.

if [ -z "${ABE_ICF}" ]; then
  ${LUPRINTF} -Eelp2 "`gettext 'You must use the <-a> option to specify the ICF file of the ABE.'`"
  usage 3
fi

if [ -z "${PBE_ICF}" ]; then
  ${LUPRINTF} -Eelp2 "`gettext 'You must use the <-p> option to specify the ICF file of the PBE.'`"
  usage 3
fi

# Validate the ABE ICF file.

lulib_icf_validate "${ABE_ICF}"
if [ "$?" -ne "0" ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'The file <%s> specified by the <-a> option is not a valid ICF file.'`" "${ABE_ICF}"
  usage 3
fi

# Validate the PBE ICF file.

lulib_icf_validate "${PBE_ICF}"
if [ "$?" -ne "0" ] ; then
  ${LUPRINTF} -Eelp2 "`gettext 'The file <%s> specified by the <-p> option is not a valid ICF file.'`" "${PBE_ICF}"
  usage 3
fi

# signal handling for cleanup.
# 1- SIGHUP (hangup)
# 2- SIGINT (user interrupt)
# 3- SIGQUIT (user quit)
# 9- SIGKILL (kill process - can not be caught or ignored :)
# 15- SIGTERM (software termination request)
trap "interruptHandler" 1 2 3 9 15


PBE_NAME=`nawk -F: 'NR == 1 { print $1 }' $PBE_ICF`
ABE_NAME=`nawk -F: 'NR == 1 { print $1 }' $ABE_ICF`

# 1. Determine each of the non-shared PBE file system and snapshot/clone

(
SAVEIFS="$IFS"
${LUETCBIN}/ludo filter_shared_and_swap $PBE_ICF $ABE_ICF |
while IFS=: read be_name mntpt dataset fstyp blocks zonename; do

  #    When looking up the mount point, file system type, and block size of
  #    a file system, the ICF file is scanned - its format is:
  #	be_name:mount_point:dataset_path:zfs:block_size[:zonename]
  #    for ZFS entries.

  IFS="$SAVEIFS"
	
  if [ -z "$dataset" -o -z "$fstyp" -o -z "$blocks" -o -z "$mntpt" ]; then
    ${LUPRINTF} -Eelp2 "`gettext 'The ABE ICF file <%s> contains invalid contents.'`" "${ABE_ICF}"
    exit 1
  fi
 
  if [ "$zonename" = "" ]; then
      zonename=global
  fi

  # Get the PBE name of the dataset in order to do the snapshot
  pbe_dataset=`/bin/nawk -F: -v MP="$mntpt" '$2 == MP { print $3 }' $PBE_ICF`
  if [ -z "$pbe_dataset" ] ; then
    ${LUPRINTF} -Eelp2 "`gettext 'Unable to get the dataset for mount point <%s> from the PBE ICF <%s>'`" $mntpt $PBE_ICF
    exit 1
  fi

  #
  # If the dataset/snapshot exist then delete them
  #
  lulib_dataset_exists $dataset
  if [ $? -eq 1 ] ; then
      if [ -n "$flag_i" ] ; then 
          ${LUPRINTF} -Eelp2 "`gettext 'Unable to create snapshot and clone of <%s> for ABE <%s>: <%s> already exists'`" "$pbe_dataset" "$ABE_NAME" "$dataset"
          exit 1
      else
          lulib_demote_and_destroy_dataset $dataset
          if [ $? -ne 0 ] ; then
              ${LUPRINTF} -Eelp2 "`gettext 'Unable to create snapshot and clone of <%s> for ABE <%s>: <%s> already exists. Attempt to destroy failed.'`" "$pbe_dataset" "$ABE_NAME" "$dataset"
              exit 1
          fi
      fi 
  fi

  # Snapshot and clone the dataset
  lulib_clone_BE_dataset "$pbe_dataset" "$ABE_NAME" "$dataset"

  # Set the canmount property to noauto
  COMMAND="zfs set canmount=noauto $dataset"
  ${LUPRINTF} -lp2D - \
      "`gettext 'Executing ZFS set command: <%s>.'` " "$COMMAND"
  ${LUPRINTF} -lp1 \
      "`gettext 'Setting canmount=noauto for <%s> in zone <%s> on <%s>.'`" \
      $mntpt $zonename $dataset

  ERRMSG="`/sbin/sh -c \"$COMMAND\" 2>&1`"

  if [ $? -ne 0 ]; then
    ${LUPRINTF} -Eelp2 '%s' "${ERRMSG}"
    ${LUPRINTF} -Eelp2 "`gettext 'Unable to set canmount property for <%s> \
file system for <%s> in zone <%s> on <%s>.'`" $fstyp $mntpt $zonename $dataset
    exit 1
  fi
  ${LUPRINTF} -lp2D - '%s' "${ERRMSG}"

done
)

res="$?"

if [ "$res" != "0" ]; then
	exit_script $res
fi

# 2. Snapshot/clone any zones in the BE


pbe_dataset=`lulib_get_root_dataset_from_icf $PBE_ICF`
pbe_mounted=0
lulib_dataset_mounted "$pbe_dataset"
if [ $? = 0 ]; then
	errfile=/tmp/.luclone.$$.err
	pbe_mntpt=`$LUBIN/lumount -i "$PBE_ICF" 2> $errfile`
	if [ $? != 0 ]; then
		[ -s "$errfile" ] && $LUPRINTF -elp2 '%R' < "$errfile"
		$LUPRINTF -Eelp2 "`gettext 'Unable to mount PBE <%s>'`" "$PBE_NAME"
		exit_script 1
	fi
	pbe_mounted=1
else
	pbe_mntpt=`nawk -v DS="$pbe_dataset" '$1==DS {print $2}' < /etc/mnttab`
fi

abe_mntpt=`$LUBIN/lumount -Z -i "$ABE_ICF" 2> $errfile`
if [ $? != 0 ]; then
	[  -s "$errfile" ] && $LUPRINTF -elp2 '%R' < "$errfile"
	$LUPRINTF -Eelp2 "`gettext 'Unable to mount ABE <%s>'`" "$ABE_NAME"
	if [ $pbe_mounted = 1 ]; then
		$LUBIN/luumount -f -i "$PBE_ICF" > /dev/null 2>&1
	fi
	exit_script 1
fi

if [ -f "$pbe_mntpt/etc/zones/lu_suffix" ]; then
	STRIPSFX="-"`cat "$pbe_mntpt/etc/zones/lu_suffix"`
else
	STRIPSFX="-$PBE_NAME"
fi

ADDSFX="$ABE_NAME"
pbe_base=`/bin/basename "$pbe_dataset"`
zfs list -t filesystem -rHo name "$pbe_dataset" > /tmp/.luclonefs.$$.dslist

all_nonglobal_zones "$pbe_mntpt" | while read zonename; do
	# Find the zonepath in the PBE
	pbe_zp=`/usr/sbin/zonecfg -R "$pbe_mntpt" -z $zonename info zonepath |
	    /bin/nawk '{print $2}'`

	pbe_zset=`lulib_get_zfs_dataset_from_mntpt "$pbe_zp"`
	if [ -z "$pbe_zset" ]; then
		# not in a zfs dataset - we need to copy instead

		lulib_copy_zone_to_dataset $zonename "$pbe_mntpt" "$PBE_NAME" "$abe_mntpt" "$ABE_NAME" "$ABE_ICF"
		continue
	fi

	# Find the zone dataset (if any) in the PBE
	dataset_in_pbe=`fgrep -x "$pbe_zset" /tmp/.luclonefs.$$.dslist`
	if [ -n "$dataset_in_pbe" ]; then
		# The zone dataset is in the same hierarchy as the BE,
		# so we can just create a new name in there.
		abe_zset=`echo $pbe_zset |
		    sed "s+/ROOT/${pbe_base}/+/ROOT/${ABE_NAME}/+;s/$STRIPSFX"'$//;s/$/-'"$ADDSFX/"`
	else
		# we're in a separate hierarchy, so just swap suffixes
		abe_zset=`echo $pbe_zset |
		    sed "s/$STRIPSFX"'$//;s/$/-'"$ADDSFX/"`
	fi

	lulib_dataset_exists "$abe_zset"
	if [ $? -eq 1 ]; then
		lulib_demote_and_destroy_dataset "$abe_zset"
	fi

	lulib_clone_BE_dataset "$pbe_zset" "$ABE_NAME" "$abe_zset"
	/sbin/zfs set zpdata:rbe="$ABE_NAME" "$abe_zset"
	/sbin/zfs set zpdata:zn="$zonename" "$abe_zset"

	pbe_rawzp=`echo "$pbe_zp"|sed "s:^$pbe_mntpt/:/:"`
	mount_prop=`zfs get -Ho value mountpoint "$pbe_zset"`
	if [ $mount_prop = "legacy" ]; then
		newpath=`echo "${abe_mntpt}${pbe_rawzp}"|
		    sed "s/$STRIPSFX"'$//;s/$/-'"$ADDSFX/"`
		newrawpath=`lulib_resolve_lofs "$pbe_mntpt" "$newpath"`
		[ ! -d ${newrawpath} ] && mkdir -p ${newrawpath}
		/sbin/mount -F zfs "$abe_zset" "$newrawpath"
	else
		newpath=`/sbin/zfs get -Ho value mountpoint "$abe_zset"`
		newrawpath="$newpath"
		/sbin/zfs mount "$abe_zset"
	fi
	if [ $? -ne 0 ]; then
		$LUPRINTF -Eelp2 "`gettext 'Failed to mount dataset <%s>'`" "$abe_zset"
	else
		echo "$ABE_NAME" > "$newrawpath/lu_moved"
	fi
	if [ -n "$dataset_in_pbe" ]; then
		/sbin/zfs set canmount=noauto "$abe_zset"
	else
		/sbin/zfs set canmount=off "$abe_zset"
		[ $mount_prop != "legacy" ] && newpath="$abe_mntpt$newpath"
	fi
	zonecfg -R "$abe_mntpt" -z $zonename set -F zonepath="$newpath"
done
rm -f /tmp/.luclonefs.$$.dslist

if [ $pbe_mounted = 1 ]; then
	$LUBIN/luumount -f -i "$PBE_ICF"
fi

$LUBIN/luumount -f -i "$ABE_ICF"

exit_script $res
