#!/bin/sh

. /lib/partman/lvm_tools.sh

#####################################
#
# Functions
#
#####################################

do_initial_setup() {
	# load required kernel modules
	depmod -a >/dev/null 2>&1
	modprobe dm-mod >/dev/null 2>&1
	modprobe lvm-mod >/dev/null 2>&1

	# make sure that lvm is available
	if ! grep -q "[0-9] device-mapper$" /proc/misc ; then
		db_input critical partman-lvm/nolvm
		db_go
		exit 0
	fi

	# scan for logical volumes
	log-output -t partman-lvm pvscan
	log-output -t partman-lvm vgscan

	# Commit the changes
	confirm_changes "partman-lvm" || exit 0
	for s in /lib/partman/commit.d/*; do
		if [ -x $s ]; then
			$s || {
				db_input critical partman-lvm/commit_failed || true
				db_go || true
				for s in /lib/partman/init.d/*; do
					if [ -x $s ]; then
						$s || exit 255
					fi
				done
				exit 0
			}
		fi
	done

	# initialize (pvcreate) all volumes that are not already prepared
	for pv in $(pv_list); do
		if ! pv_create "$pv"; then
			db_subst partman-lvm/pvcreate_error PV "$pv"
			db_input critical partman-lvm/pvcreate_error
			db_go || true
			exit 0
		fi
	done

	if [ ! -f /var/cache/partman-lvm/first ]; then
		# try to activate old volume groups
		count=$(vg_list | wc -l)
		if [ $count -gt 0 ]; then
			db_subst partman-lvm/activevg COUNT $count
			db_set partman-lvm/activevg "false"
			db_input critical partman-lvm/activevg
			db_go || true
			db_get partman-lvm/activevg
			[ "$RET" = "true" ] && log-output -t partman-lvm vgchange -a y
			# TODO: We need to update the devices that LVM just claimed
		fi
		# ask only the first time
		mkdir -p /var/cache/partman-lvm && touch /var/cache/partman-lvm/first
	fi
}

do_display() {
	lvm_get_config
	db_subst partman-lvm/displayall CURRENT_CONFIG "$RET"
	db_input critical partman-lvm/displayall
	db_capb
	db_go || true
	db_capb backup
	db_get partman-lvm/displayall
}

do_vg_create() {
	local pvs pv output vg
	pvs=""

	# Look for free PVs
	for pv in $(pv_list_free); do
		pv_get_info "$pv"
		output=$(printf "%-30s (%sMB)" "$pv" "$SIZE")
		if [ -z "$pvs" ]; then
			pvs="$output"
		else
			pvs="$pvs, $output"
		fi
	done
	if [ -z "$pvs" ]; then
		db_input critical partman-lvm/nopartitions
		db_go || true
		return
	fi

	# Prompt for VG name
	db_set partman-lvm/vgcreate_name ""
	db_input critical partman-lvm/vgcreate_name
	db_go || return
	db_get partman-lvm/vgcreate_name
	vg="$RET"

	# Check VG name
	if [ -z "$vg" ]; then
		db_input critical partman-lvm/vgcreate_nonamegiven
		db_go || true
		return
	fi

	if ! vg_name_ok "$vg"; then
		db_input critical partman-lvm/badnamegiven
		db_go || true
		return
	fi

	# Check whether the VG name is already in use
	if vgs "$vg" > /dev/null 2>&1; then
		db_input critical partman-lvm/vgcreate_nameused
		db_go || true
		return
	fi

	# Check whether the VG name overlaps with an existing device
	if [ -e "/dev/$vg" ]; then
		db_input critical partman-lvm/vgcreate_devnameused
		db_go || true
		return
	fi

	# Choose the PVs to use
	db_subst partman-lvm/vgcreate_parts PARTITIONS "$pvs"
	db_reset partman-lvm/vgcreate_parts
	db_input critical partman-lvm/vgcreate_parts
	db_go || return
	db_get partman-lvm/vgcreate_parts
	if [ -z "$RET" ]; then
		db_input critical partman-lvm/vgcreate_nosel
		db_go || true
		return
	fi
	pvs=$(echo "$RET" | sed -e 's/ *([^)]*) *//g')
	pvs=$(csv_to_ssv "$pvs")

	if ! vg_create "$vg" $pvs; then
		db_subst partman-lvm/vgcreate_error VG "$vg"
		db_input critical partman-lvm/vgcreate_error
		db_go || true
	else
		db_subst partman-lvm/text/in_use VG "$vg"
		db_metaget partman-lvm/text/in_use description
		for pv in $pvs; do
			partman_lock_unit "$pv" "$RET"
		done
	fi
}

do_vg_delete() {
	local vgs vg output pvs
	vgs=""

	# Look for VGs with no LVs
	for vg in $(vg_list); do
		vg_get_info "$vg"
		[ "$LVS" -eq 0 ] || continue
		output=$(printf "%-30s (%sMB)" "$vg" "$SIZE")
		if [ -z "$vgs" ]; then
			vgs="$output"
		else
			vgs="$vgs, $output"
		fi
	done
	if [ -z "$vgs" ]; then
		db_input critical partman-lvm/vgdelete_novg
		db_go || true
		return
	fi

	# Prompt for VG to delete
	db_subst partman-lvm/vgdelete_names GROUPS "$vgs"
	db_reset partman-lvm/vgdelete_names
	db_input critical partman-lvm/vgdelete_names
	db_go || return
	db_get partman-lvm/vgdelete_names
	vg=$(echo "$RET" | sed -e 's/[[:space:]]*(.*//')

	# Confirm
	db_subst partman-lvm/vgdelete_confirm VG $vg
	db_set partman-lvm/vgdelete_confirm "false"
	db_input critical partman-lvm/vgdelete_confirm
	db_go || true
	db_get partman-lvm/vgdelete_confirm
	[ "$RET" = "true" ] || return

	pvs=$(vg_list_pvs "$vg")
	if ! vg_delete "$vg"; then 
		db_input critical partman-lvm/vgdelete_error
		db_go || true
	else
		for pv in $pvs; do
			partman_unlock_unit "$pv"
		done
	fi
}

do_vg_extend() {
	local pvs pv output vgs vg
	vgs=""

	# Get eligible PVs
	pvs=""
	for pv in $(pv_list_free); do
		pv_get_info "$pv"
		output=$(printf "%-30s (%sMB)" "$pv" "$SIZE")
		if [ -z "$pvs" ]; then
			pvs="$output"
		else
			pvs="$pvs, $output"
		fi
	done
	if [ -z "$pvs" ]; then
		db_input critical partman-lvm/nopartitions
		db_go || true
		return
	fi

	# Get VG list
	vgs=""
	for vg in $(vg_list); do
		vg_get_info "$vg"
		output=$(printf "%-30s (%sMB)" "$vg" "$SIZE")
		if [ -z "$vgs" ]; then
			vgs="$output"
		else
			vgs="$vgs, $output"
		fi
	done
	if [ -z "$vgs" ]; then
		db_input critical partman-lvm/vgextend_novg
		db_go || true
		return
	fi

	# Prompt for VG to extend
	db_subst partman-lvm/vgextend_names GROUPS "$vgs"
	db_reset partman-lvm/vgextend_names
	db_input critical partman-lvm/vgextend_names
	db_go || return
	db_get partman-lvm/vgextend_names
	vg=$(echo "$RET" | sed -e 's/[[:space:]]*(.*//')

	# Prompt for PVs to use
	db_subst partman-lvm/vgextend_parts PARTITIONS "$pvs"
	db_reset partman-lvm/vgextend_parts
	db_input critical partman-lvm/vgextend_parts
	db_go || return
	db_get partman-lvm/vgextend_parts
	if [ -z "$RET" ]; then
		db_input critical partman-lvm/vgextend_nosel
		db_go || true
		return
	fi
	pvs=$(echo "$RET" | sed -e 's/ *([^)]*) *//g')
	pvs=$(csv_to_ssv "$pvs")

	for pv in $pvs; do
		if ! vg_extend "$vg" "$pv"; then
			db_subst partman-lvm/vgextend_error PARTITION $pv
			db_subst partman-lvm/vgextend_error VG $vg
			db_input critical partman-lvm/vgextend_error
			db_go || true
			return
		else
			db_subst partman-lvm/text/in_use VG "$vg"
			db_metaget partman-lvm/text/in_use description
			partman_lock_unit "$pv" "$RET"
		fi
	done
}

do_vg_reduce() {
	local vgs vg output pvs pv

	# Check for VGs with more than one pv
	vgs=""
	db_metaget partman-lvm/text/pvs description
	for vg in $(vg_list); do
		vg_get_info "$vg"
		[ "$PVS" -gt 1 ] || continue
		output=$(printf "%-30s (%sMB - %s)" "$vg" "$FREE" "$PVS $RET")
		if [ -z "$vgs" ]; then
			vgs="$output"
		else
			vgs="$vgs, $output"
		fi
	done
	if [ -z "$vgs" ]; then
		db_input critical partman-lvm/vgreduce_novg
		db_go || true
		return
	fi

	# Prompt for VG to reduce
	db_subst partman-lvm/vgreduce_names GROUPS "$vgs"
	db_reset partman-lvm/vgreduce_names
	db_input critical partman-lvm/vgreduce_names
	db_go || return
	db_get partman-lvm/vgreduce_names
	vg=$(echo "$RET" | sed -e 's/[[:space:]]*(.*//')

	# Prompt for PV to remove
	pvs=""
	for pv in $(vg_list_pvs "$vg"); do
		pv_get_info "$pv"
		output=$(printf "%-30s (%sMB)" "$pv" "$SIZE")
		if [ -z "$pvs" ]; then
			pvs="$output"
		else
			pvs="$pvs, $output"
		fi
	done

	# Prompt for PVs to use
	db_subst partman-lvm/vgreduce_parts PARTITIONS "$pvs"
	db_reset partman-lvm/vgreduce_parts
	db_input critical partman-lvm/vgreduce_parts
	db_go || return
	db_get partman-lvm/vgreduce_parts
	if [ -z "$RET" ]; then
		db_input critical partman-lvm/vgreduce_nosel
		db_go || true
		return
	fi
	pvs=$(echo "$RET" | sed -e 's/ *([^)]*) *//g')
	pvs=$(csv_to_ssv "$pvs")

	# Check if all PVs were selected and delete in that case
	count=$(echo -n "$pvs" | wc -w)
	vg_get_info "$vg"
	if [ "$count" -eq "$PVS" ]; then
		if ! vg_delete "$vg"; then 
			db_input critical partman-lvm/vgdelete_error
			db_go || true
		fi
		return
	fi

	# Go for it
	for pv in $pvs; do
		if ! vg_reduce "$vg" "$pv"; then
			db_subst partman-lvm/vgreduce_error VG "$vg"
			db_subst partman-lvm/vgreduce_error PARTITION "$pv"
			db_input critical partman-lvm/vgreduce_error
			db_go || true
			return
		else
			partman_unlock_unit "$pv"
		fi
	done
}

do_lv_create() {
	local vgs vg output lv size max_size

	# Find eligible VGs
	vgs=""
	for vg in $(vg_list_free); do
		vg_get_info "$vg"
		output=$(printf "%-30s (%sMB)" "$vg" "$FREE")
		if [ -z "$vgs" ]; then
			vgs="$output"
		else
			vgs="$vgs, $output"
		fi
	done

	if [ -z "$vgs" ]; then
		db_input critical partman-lvm/lvcreate_nofreevg
		db_go || true
		return
	fi

	# Prompt for VG to use
	db_subst partman-lvm/lvcreate_vgnames GROUPS "$vgs"
	db_reset partman-lvm/lvcreate_vgnames
	db_input critical partman-lvm/lvcreate_vgnames
	db_go || return
	db_get partman-lvm/lvcreate_vgnames
	vg=$(echo "$RET" | sed -e 's/[[:space:]]*(.*//')

	# Prompt for name to give the new lv
	db_set partman-lvm/lvcreate_name ""
	db_input critical partman-lvm/lvcreate_name
	db_go || return
	db_get partman-lvm/lvcreate_name
	lv="$RET"

	# Check LV name
	if [ -z "$lv" ]; then
		db_input critical partman-lvm/lvcreate_nonamegiven
		db_go || true
		return
	fi

	if ! lv_name_ok "$lv"; then
		db_input critical partman-lvm/badnamegiven
		db_go || true
		return
	fi

	# Make sure the name isn't already in use
	if lvs "/dev/$vg/$lv" > /dev/null 2>&1; then
		db_subst partman-lvm/lvcreate_exists LV $lv
		db_subst partman-lvm/lvcreate_exists VG $vg
		db_input critical partman-lvm/lvcreate_exists
		db_go || true
		return
	fi

	# Prompt for lv size
	vg_get_info "$vg"
	max_size="${FREE}M"
	db_set partman-lvm/lvcreate_size "${max_size}B"
	db_fset partman-lvm/lvcreate_size seen false
	db_input critical partman-lvm/lvcreate_size
	db_go || return
	db_get partman-lvm/lvcreate_size
	[ -n "$RET" ] || return
	size=$(lvm_size_from_human "$RET")

	# If the maximum free space should be used for the new LV, use the
	# number of physical extents (PEs) because the size given by LVM
	# might not be accurate, resulting in an error because the VG is
	# not big enough (see #250594).
	if [ "$size" = "$max_size" ]; then
		size="full"
	fi

	if ! lv_create "$vg" "$lv" "$size"; then
		db_subst partman-lvm/lvcreate_error VG $vg
		db_subst partman-lvm/lvcreate_error LV $lv
		db_subst partman-lvm/lvcreate_error SIZE $RET
		db_input critical partman-lvm/lvcreate_error
		db_go || true
		return
	fi
}

do_lv_delete() {
	local lvs line output lv vg

	lvs=""
	for line in $(lv_list); do
		lv=$(echo "$line" | cut -d':' -f1)
		vg=$(echo "$line" | cut -d':' -f2)
		db_subst partman-lvm/text/lvdelete_invg VG "$vg"
		db_metaget partman-lvm/text/lvdelete_invg description
		lv_get_info "$vg" "$lv"
		output=$(printf "%-30s (%sMB - %s)" "$lv" "$SIZE" "$RET")
		if [ -z "$lvs" ]; then
			lvs="$output"
		else
			lvs="$lvs, $output"
		fi
	done

	if [ -z "$lvs" ]; then
		db_input critical partman-lvm/lvdelete_nolv
		db_go || true
		return
	fi

	db_subst partman-lvm/lvdelete_lvnames LVS "$lvs"
	db_reset partman-lvm/lvdelete_lvnames
	db_input critical partman-lvm/lvdelete_lvnames
	db_go || return
	db_get partman-lvm/lvdelete_lvnames
	lv=$(echo "$RET" | cut -d' ' -f1)
	vg=$(echo "$RET" | sed -e 's/.*(\(.*\))/\1/')
	vg=${vg##* }

	if ! lv_delete "$vg" "$lv"; then
		db_subst partman-lvm/lvdelete_error VG $vg
		db_subst partman-lvm/lvdelete_error LV $lv
		db_input critical partman-lvm/lvdelete_error
		db_go || true
		return
	fi
}

#####################################
#
# Main stuff
#
#####################################

do_initial_setup

# Prepare menu choice translations
db_metaget partman-lvm/menu/display description
menu_display="$RET"
db_metaget partman-lvm/menu/createvg description
menu_createvg="$RET"
db_metaget partman-lvm/menu/deletevg description
menu_deletevg="$RET"
db_metaget partman-lvm/menu/extendvg description
menu_extendvg="$RET"
db_metaget partman-lvm/menu/reducevg description
menu_reducevg="$RET"
db_metaget partman-lvm/menu/createlv description
menu_createlv="$RET"
db_metaget partman-lvm/menu/deletelv description
menu_deletelv="$RET"
db_metaget partman-lvm/menu/finish description
menu_finish="$RET"

while [ 1 ]; do
	# Get some statistics
	free_pvs=$(pv_list_free | wc -l)
	used_pvs=$(pvs --noheadings | wc -l)  # All PVs
	used_pvs=$(( $used_pvs - $free_pvs )) # Used = All PVs - Free PVs
	vgs=$(vg_list | wc -l)
	lvs=$(lv_list | wc -l)

	# Build menu options
	choices="$menu_display"
	# Create VG
	if [ $free_pvs -gt 0 ]; then
		choices="${choices}, $menu_createvg"
	fi
	# Other options only if there is at least one VG
	if [ $vgs -gt 0 ]; then
		# First check, then add so the order is constant
		do_reducevg=""
		do_deletevg=""
		do_createlv=""
		# Check all VGs
		for vg in $(vg_list); do
			vg_get_info "$vg"
			if [ $PVS -gt 1 ]; then
				do_reducevg="1"
			fi
			if [ $LVS -eq 0 ]; then
				do_deletevg="1"
			fi
			if [ $FREEPE -gt 0 ]; then
				do_createlv="1"
			fi
		done

		# Create LV
		if [ -n "$do_createlv" ]; then
			choices="${choices}, $menu_createlv"
		fi
		# Delete VG
		if [ -n "$do_deletevg" ]; then
			choices="${choices}, $menu_deletevg"
		fi
		# Delete LV
		if [ $lvs -gt 0 ]; then
			choices="${choices}, $menu_deletelv"
		fi
		# Extend VG
		if [ $free_pvs -gt 0 ]; then
			choices="${choices}, $menu_extendvg"
		fi
		# Reduce VG
		if [ -n "$do_reducevg" ]; then
			choices="${choices}, $menu_reducevg"
		fi
	fi
	# Add Finish option
	choices="${choices}, $menu_finish"

	# Setup main menu template
	db_subst partman-lvm/mainmenu CHOICES "$choices"
	db_subst partman-lvm/mainmenu FREE_PVS "$free_pvs"
	db_subst partman-lvm/mainmenu USED_PVS "$used_pvs"
	db_subst partman-lvm/mainmenu VGS "$vgs"
	db_subst partman-lvm/mainmenu LVS "$lvs"
	db_reset partman-lvm/mainmenu
	db_input critical partman-lvm/mainmenu
	db_go || break

	db_get partman-lvm/mainmenu
	option="$RET"
	if [ "$option" = "$menu_display" ]; then
		do_display
	elif [ "$option" = "$menu_createvg" ]; then
		do_vg_create
	elif [ "$option" = "$menu_deletevg" ]; then
		do_vg_delete
	elif [ "$option" = "$menu_extendvg" ]; then
		do_vg_extend
	elif [ "$option" = "$menu_reducevg" ]; then
		do_vg_reduce
	elif [ "$option" = "$menu_createlv" ]; then
		do_lv_create
	elif [ "$option" = "$menu_deletelv" ]; then
		do_lv_delete
	elif [ "$option" = "$menu_finish" ]; then
		break
	else
		logger -t partman-lvm "Unknown selection '$option'"
		break
	fi
done

# install lvm tools in /target if needed
if [ $(lv_list | wc -l) -gt 0 ]; then
	apt-install lvm2
fi

stop_parted_server

restart_partman

exit 0
