#!/sbin/sh -
# %W% %G% %U% - %Q%
#ident "%Z%unixvm:%M%	%I%"

# Copyright (c) 2000 VERITAS Software Corporation.  ALL RIGHTS RESERVED.
# UNPUBLISHED -- RIGHTS RESERVED UNDER THE COPYRIGHT
# LAWS OF THE UNITED STATES.  USE OF A COPYRIGHT NOTICE
# IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
# OR DISCLOSURE.
# 
# THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND
# TRADE SECRETS OF VERITAS SOFTWARE.  USE, DISCLOSURE,
# OR REPRODUCTION IS PROHIBITED WITHOUT THE PRIOR
# EXPRESS WRITTEN PERMISSION OF VERITAS SOFTWARE.
# 
#               RESTRICTED RIGHTS LEGEND
# USE, DUPLICATION, OR DISCLOSURE BY THE GOVERNMENT IS
# SUBJECT TO RESTRICTIONS AS SET FORTH IN SUBPARAGRAPH
# (C) (1) (ii) OF THE RIGHTS IN TECHNICAL DATA AND
# COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013.
#               VERITAS SOFTWARE
# 1600 PLYMOUTH STREET, MOUNTAIN VIEW, CA 94043

__VXVM_CONNECT_WAIT=WAIT
export __VXVM_CONNECT_WAIT

: ${VOLROOT_DIR:=$__VXVM_ROOT_DIR}
. ${VOL_SCRIPTS_LIB:-$VOLROOT_DIR/usr/lib/vxvm/lib}/vxcommon

prog=vxunreloc

usage()
{
	export prog; egettxt >&2 \
"Usage: $prog [-f] [-g diskgroup] [-t tasktag] [-n new_dmname] origin_dmname" \
	vxvmshm:984
	exit 1
}

quit()
{
	rm -f $tmpfile
	exit $1
}

do_force_mv()
{

	is_rootdisk=0

	trap "rm -f $mv_vol_list $sort_sd_list" 1 2 3
	rm -f  $mv_vol_list $sort_sd_list

	# newsd_list contains all the subdisks to be unrelocated
	# the file has three columns: sd name, orig_dmoffset, len
	# sort it based on the original offset value

	sort -k 2,2n $newsd_list > $sort_sd_list
	 
	# get dm name and volume name for all the subdisks which 
	# are to be unrelocated
	# eliminate duplicated volumes
	
	touch $mv_vol_list
	cat $sort_sd_list | \
	while read sd_name origoffset len 
	do
		dm_name=`vxprint -g $dg_name -s -F %dm_name $sd_name`
		v_name=`vxprint -g $dg_name -s -F %v_name $sd_name`
		grep $v_name $mv_vol_list 2> $1 > /dev/null
		if [ $? -eq 1 ]
		then
			echo $dm_name $v_name >>$mv_vol_list
		fi
	done
	# attempt unrelocation of all volumes in the list
	if [ -s $mv_vol_list ]
	then
		cat $mv_vol_list | \
		while read dm_name v_name
		do
			vxassist -g $dg_name $taskopt -u $orig_dmname move $v_name !$dm_name $dst_dmname
			ret=$?
			if [ $ret -ne 0 ]
			then
				export prog v_name dst_dmname; egettxt >&2 \
"Error: $prog Volume $v_name failed to be unrelocated to $dst_dmname,
please correct the problem and run vxunreloc again. \\n" vxvmshm:917
				quit $ret
			fi
			# run vxbootsetup to create hard partitions for
			# swap and root
			if [ X$v_name = X`vxprint -AQve '(v_use_type="root" || v_use_type="swap")
					&& v_name="'$v_name'"' -F %name` ]
			then
				is_rootdisk=1
			fi

		done
		if [ $is_rootdisk -eq 1 ]
		then
			/etc/vx/bin/vxbootsetup $dst_dmname
		fi
	else
		export prog dst_dmname; egettxt >&2 \
"Error: $prog No Volume to be unreloated to $dst_dmname" vxvmshm:913,
		quit 11
	fi
	
	quit 0
}

try_alloc_sd()
{
	prev_offset=0
	prev_len=0

	cat $existsd_list | \
	while true 
	do
		read e_name e_offset e_len
		if [ -z "$e_name" ]
		then
			if [ `expr $prev_offset + $prev_len` -gt "$n_offset" ]
			then
				return 1
			else
				return 0
			fi
		fi
		if [ "$n_offset" -lt "$e_offset" ]
 		then
 			if [ `expr $n_offset + $n_len` -le "$e_offset" -a \
 			  `expr $prev_offset + $prev_len` -le "$n_offset" ]

 			then
				return 0
 			else
 				return 1
 			fi
 		else
 			prev_offset=$e_offset
 			prev_len=$e_len
 		fi
	done
}



get_colnum_in_sd()
{
	_sd_name=$1

	_v_name=`vxprint -g $dg_name -s -F '%v_name' $_sd_name`
	_is_subvol=`vxprint -g $dg_name -m $_v_name | grep "layered=" | awk -F= '{print $2}'`

	if [ $_is_subvol = "off" ]
	then
		curr_colnum=`vxprint -g $dg_name -m $_sd_name | grep "column=" | awk -F= '{print $2}'`
	else
		curr_colnum=`vxprint -g $dg_name -Qs -e 'sd_dm_name="'$_v_name'"' -F '%column'`
	fi
}


dg_name=rootdg
force_mv=0
dst_dmname=
orig_dmname=
try_force=0

# the following are the temporary files used in the program
newsd_list=/tmp/ur-nsd$$
existsd_list=/tmp/ur-esd$$
makesd_list=/tmp/ur-mksd$$
mvsd_list=/tmp/ur-mvsd$$
mvsd_sort_list=/tmp/ur-sort$$
mv_vol_list=/tmp/ur-mvvol$$
sort_sd_list=/tmp/ur-stsd$$
tmpfile="$newsd_list $existsd_list $mvsd_list $makesd_list $mvsd_sort_list $mv_vol_list $sort_sd_list"

trap "rm -f $tmpfile" 1 2 3 15
set_OS_variables

taskopt=""
while getopts fg:n:t: c
do
	case $c in
	f)	force_mv=1;;
	g)	dg_name=$OPTARG
		;;
	n)	dst_dmname=$OPTARG
		;;
	t)	#tasktag=$OPTARG
		taskopt="-t $OPTARG"
		;;
	\?)	usage;;
	esac
done

shift `expr $OPTIND - 1`
if [ $# -ne 1 ]
then
	usage
fi

orig_dmname=$@
if [ "$dst_dmname" = "" ]
then
	dst_dmname=$orig_dmname
fi
shift $#


# check if the destination disk is a valid disk

vxprint -g $dg_name -qtd | awk '$1=="dm" && $3!="-" {print $2;}'\
 | grep "$dst_dmname" > /dev/null 2> /dev/null

if [ $? != 0 ] 
then
	export prog dst_dmname; egettxt >&2 \
"Error: $prog disk media name $dst_dmname does not appear to be valid." \
	vxvmshm:918
	quit 11
fi


# get subdisks in the specified disk group based on the following 
# criteria: 
#
#	1. orig_dmname matches $orig_dmname, meaning the subdisk
#	   was relocated from that disk
#	2. dm_name != $dst_dmname, meaning the subdisk was not yet
#	   'unrelocated'
#
#	   the following three criteria are vxsd's restriction:
#
#	3. subdisk must be associated with a plex and a volume
#	4. the associated plex can not be in DISABLED state
#	5. the associated volume can not be in DISABLED state


rm -rf $newsd_list
vxprint -Qs -g $dg_name -e 'sd_orig_dmname="'$orig_dmname'" &&
	sd_dm_name!="'$dst_dmname'" &&
	assoc!="" &&
	assoc->pl_kstate!=DISABLED && assoc.assoc!="" &&
	assoc.assoc->v_kstate!=DISABLED' \
	-F '%name %orig_dmoffset %len' > $newsd_list

# if there aren't any subdisks to be moved back to the destination
# disk, then print a message and exit!

if [ ! -s $newsd_list ]
then
	export prog orig_dmname; egettxt >&2 \
"Error: $prog No subdisks were found which originally were relocated from $orig_dmname" \
	vxvmshm:914
	quit 11
fi


# make sure that the destination disk is large enough to hold all the 
# subdisks

req_size=0
exec 3<&0 < $newsd_list
while read name offset len
do
	req_size=`expr $req_size + $len`
done
exec 0<&3 3<&-

# calculate the free space on the destination disk
sum=0
total=`vxdg -qa -g $dg_name free $dst_dmname | \
	awk '{sum += $5}; END {printf("%ld\n", sum)}'`

# if the free space is less than the required size, print an error message
# and then exit
if [ $total -lt $req_size ]
then
	export prog dst_dmname; egettxt >&2 \
"Error: $prog Disk $dst_dmname is not large enough to accomodate all the relocated subdisks." \
	vxvmshm:911
	quit 20
fi

rm -rf $existsd_list
# get existing subdisks on the destination disk
vxprint -Qs -g $dg_name -e 'sd_dm_name="'$dst_dmname'"' \
	-F '%name %offset %len' > $existsd_list

# test if the new subdisk will colide with any of the existing subdisks
n_offset=0
n_len=0
e_offset=0
e_len=0
seq=0

rm -f $makesd_list $mvsd_list
exec 3<&0 <$newsd_list
while read n_name n_offset n_len
do
	# first, test if an sd, which has same offset, and len
	# already existed on dst_disk. if it does,
	# it is possible that system crashed between "vxmake sd" and 
	# "vxsd mv". in this scenario, sd was made but not moved back.
	# output n_name newsd_name n_offset n_len to $mvsd_list
	# so that later on, these subdisks can be moved back to where
	# they were from.

	newsd_name=`vxprint -g $dg_name -Qvse 'sd_dmname="'$dst_dmname'" &&
		 sd_offset='$n_offset' && 
		 sd_len='$n_len' && 
		 assoc="" &&
		 comment="UNRELOC"' -F '%name'`

	if [ ! "$newsd_name" ]
	then
		# a new sd will be created on the destination disk. 
		# verify that it will not overlap with any existing sd's

		try_alloc_sd
		if [ $? -eq 1 ]	
		then
			if [ $force_mv -eq 1 ]
			then
				try_force=1
				break
			else
				export prog n_name dst_dmname; egettxt >&2 \
"Error: $prog Subdisk $n_name can not be unrelocated to $dst_dmname 
because it would overlap with the existing subdisks" \
		vxvmshm:916
				quit 20
			fi
		fi

		# generate a name for the new sd to be created
		# the name will be in the format of "dmname-UR-seqno"

		gen_new_name=1
		dupsd=""

		while [ $gen_new_name -eq 1 -a $seq -lt 1000 ] 
		do
			seq=`expr $seq + 1`
			if [ $seq -lt 10 ]
			then
				newsd_name=${dst_dmname}-UR-00${seq}
			elif [ $seq -lt 100 ]
			then
				newsd_name=${dst_dmname}-UR-0${seq}
			else
				newsd_name=${dst_dmname}-UR-${seq}
			fi

			# if an existing sd has the same name, try to generate
			# another name
			dupsd=`vxprint -g $dg_name -Qvse 'sd_dmname="'$dst_dmname'" && \
	                 	sd_name="'$newsd_name'"' -F '%name'`
			if [ ! "$dupsd" ]
			then
				gen_new_name=0
			fi
		done

		if [ gen_new_name -eq 1 -o $seq -eq 1000 ]
		then
			export prog n_name dst_dmname;egettxt >&2 \
"Error: $prog Subdisk $n_name can not be unrelocated to $dst_dmname 
because a unique name cannot be generated" \
		vxvmshm:915
			quit 7
		fi

		# generate an entry in $makesd_list and $mvsd_list
		echo "sd $newsd_name disk=$dst_dmname offset=$n_offset \
			 len=$n_len comment=UNRELOC" >> $makesd_list
		echo $n_name $newsd_name $n_offset $n_len >> $mvsd_list

	else 
		echo $n_name $newsd_name $n_offset $n_len >> $mvsd_list
	fi

done   
exec 0<&3 3<&-


if [ $try_force -eq 1 ] 
then
	do_force_mv
	# not reached
fi

# make a list of sd's
rc=0
if [ -f $makesd_list ]
then
	vxmake -g $dg_name -d $makesd_list
	rc=$?
fi

if [ $rc -ne 0 ]
then
	[ $force_mv -eq 1 ] && do_force_mv
	quit $rc
fi

# move the list of sd's to the destination disk

is_rootdisk=0
exec 3<&0 <$mvsd_list
while read src_sd dst_sd offset len
do
	vxsd -g $dg_name $taskopt -o rm -d mv $src_sd $dst_sd
	rc=$?
	if [ $rc -ne 0 ]
	then
		export prog src_sd dst_sd rc;egettxt >&2 \
"Error: $prog Moving sd from $src_sd to $dst_sd failed, return code was $rc.
Check the error and restart $prog" \
		vxvmshm:912
		quit $rc
	fi

	# are we unrelocting a root disk ?
	v_name=`vxprint -g $dg_name -s -F "%v_name" $dst_sd`
	if [ X$v_name = X`vxprint -AQve '(v_use_type="root" || v_use_type="swap")
			&& v_name="'$v_name'"' -F %name` ]
	then
		is_rootdisk=1
	fi
done   
exec 0<&3 3<&-

# vxbootsetup has to be called to create hard partitions and 
# to write the boot block to make a disk bootable. 
if [ $is_rootdisk -eq 1 ]
then
	/etc/vx/bin/vxbootsetup $dst_dmname
fi

# get a list of subdisks whose comment field is set to "UNRELOC"
# note that any subdisks moved to the destination disk in a previous 
#      unrelocation operation which did not complete successfully
#      will be included in the list and their comment field will 
#      be cleaned up.

cleanup_list=`vxprint -Qs -g $dg_name -e 'sd_dm_name="'$dst_dmname'" && 
		sd_comment="UNRELOC"' -F %name`
for i in $cleanup_list
do
	objlist=$objlist" "$i
done
vxedit -g $dg_name set comment="" $objlist

# go through the list of subdisks to join adjacent subdisks together

sort -n -k 3 $mvsd_list > $mvsd_sort_list
prev_offset=-1
prev_len=-1
prev_vol=""
prev_plex=""
prev_sd=""
cat $mvsd_sort_list | \
while read src_sd dst_sd offset len
do

	# current sd should join the previous sd only if 
	#   1. they are adjacent to each other
	#   2. they belong to the same volume, same plex, and same column
 
	curr_vol=`vxprint -g $dg_name -s -F "%v_name" $dst_sd`
	curr_plex=`vxprint -g $dg_name -s -F "%pl_name" $dst_sd`

	# get_colnum_in_sd will set the column number in $curr_colnum
	get_colnum_in_sd $dst_sd

	if [ "$offset" -eq `expr $prev_offset + $prev_len` -a \
		"$curr_vol" = "$prev_vol" -a "$curr_plex" = "$prev_plex" -a \
		"$curr_colnum" = "$prev_colnum" ]
	then
			vxsd -g $dg_name join $prev_sd $dst_sd $prev_sd
			prev_len=`expr $prev_len + $len`
 	else
			prev_offset=$offset
			prev_len=$len
			prev_vol=$curr_vol
			prev_plex=$curr_plex
			prev_colnum=$curr_colnum
			prev_sd=$dst_sd
	fi
done

quit 0


