# vim: ft=zsh noet ts=4 sts=4 sw=4

#
# confz functions for deploying vservers
#

# load the zstat builtin and keep stat external
zmodload -F zsh/stat b:zstat

# deploy filesystem image from directory, rsync or tarball
confz_deployed_system_check() {
	checkvars path source
	defvar ignore_pattern 'lost+found'
	local existing_entries

	fail_reason="${vars[path]}/.confz_deployed missing"
	if [[ -e ${vars[path]}/.confz_deployed ]]; then
		return 0
	else
		existing_entries=( ${vars[path]}/*~${vars[path]}/${~vars[ignore_pattern]}(DN) )
		if (($#existing_entries)); then
			die "Found $#existing_entries existing entries in ${vars[path]} but no .confz_deployed"
		else
			return 1
		fi
	fi
}

confz_deployed_system_do() {
	local result
	result=0
	case ${vars[source]} in
		(*/)
			rsync -aAH ${vars[source]} ${vars[path]}/ || return $?
			;;
		(scp:*.(tbz|tbz2|bz2))
			scp -q ${vars[source]#scp:} /dev/stdout | bzip2 -d | tar -xpC ${vars[path]}
			result=$[ $pipestatus[1] | $pipestatus[2] | $pipestatus[3] ]
			;;
		(scp:*.(tgz|gz))
			scp -q ${vars[source]#scp:} /dev/stdout | gzip -d | tar -xpC ${vars[path]}
			result=$[ $pipestatus[1] | $pipestatus[2] | $pipestatus[3] ]
			;;
		(scp:*.xz)
			scp -q ${vars[source]#scp:} /dev/stdout | xz -d | tar -xpC ${vars[path]}
			result=$[ $pipestatus[1] | $pipestatus[2] | $pipestatus[3] ]
			;;
		(scp:*)
			scp -q ${vars[source]#scp:} /dev/stdout | tar -xpC ${vars[path]}
			result=$[ $pipestatus[1] | $pipestatus[2] ]
			;;
		(sftp:*)
			mkdir -p /root/.tmp || return $?
			(cd /root/.tmp && sftp ${vars[source]#sftp:}) || return $?
			tar -xpC ${vars[path]} -f /root/.tmp/${vars[source]:t}
			result=$?
			rm -f /root/.tmp/${vars[source]:t}
			;;
		(*)
			tar -xpC ${vars[path]} -f ${vars[source]} || return $?
			;;
	esac
	if (($result)); then
		return $result
	fi
	printf '%s\n' ${vars[source]} >${vars[path]}/.confz_deployed
}

# create required directories
confz_vserver_run_dirs_check() {
	local -a run_dirs
	local d
	run_dirs=( /var/run/{vservers,vservers.rev,vshelper} )
	do_command=( mkdir -p $run_dirs )
	
	for d in $run_dirs; do
		fail_reason="missing directory: ${(q)d}"
		[[ -d $d ]] || return $?
	done
	return 0
}

# configure and deploy vserver
confz_vserver_check() {
	checkvars name size context_id source
	defvar root /

	setvar etcdir ${vars[root]%/}/etc/vservers/${vars[name]}
	setvar vdir ${vars[root]%/}/vservers/${vars[name]}

	local ctx ret
	local -a fails

	if [[ -e ${vars[etcdir]}/context ]]; then
		if [[ $(<${vars[etcdir]}/context) != ${vars[context_id]} ]]; then
			die "$0: vserver ${(qqq)vars[name]} " \
				"has context different from ${(qqq)vars[context_id]}"
		fi
	else
		fails+=( "${vars[etcdir]}/context) missing" )
		for ctx in ${vars[root]%/}/etc/vservers/*/context(N); do
			if [[ $(<$ctx) == ${vars[context_id]} ]]; then
				die "$0: context id already used by $ctx"
			fi
		done
	fi
	if ! [[ -e ${vars[etcdir]}/vdir ]]; then
		fails=( "${vars[etcdir]}/vdir missing" )
	elif ! [[ -h ${vars[etcdir]}/vdir ]]; then
		die "${vars[etcdir]}/vdir is not symlink or missing"
	fi

	require mounted_volume :size \?filesystem \?mkfs_opts \
		lv_name=vs_$vars[name] \
		label=${${vars[name]}[0,12]} \
		mountpoint=$vars[vdir]

	require deployed_system :root :source path=${vars[vdir]}

	fail_reason="${(j:,:)fails}"
	return $#fails
}

confz_vserver_do() {
	mkdir -p ${vars[etcdir]} || return $?
	if [[ -h ${vars[etcdir]}/vdir ]]; then
		rm ${vars[etcdir]}/vdir || return $?
	fi
	ln -s $vars[vdir] ${vars[etcdir]}/vdir || return $?
	print -r - ${vars[context_id]} >${vars[etcdir]}/context
}

confz_vserver_started_check() {
	checkvars name
	require vserver_run_dirs
	do_command=( vserver -- $vars[name] start )
	vserver --silent -- $vars[name] running || return $?
	local context_id
	context_id=$(</etc/vservers/$vars[name]/run)
	if ! (($+vars[context_id])); then
		vars[context_id]=$context_id
	elif (( $vars[context_id] != $context_id )); then
		die "vserver ${(qqq)name} running under context id $context_id, expected $vars[context_id]"
	fi
}

confz_vserver_stopped_check() {
	checkvars name
	do_command=( vserver -- $vars[name] stop )
	vserver --silent -- $vars[name] running
	(( $? == 1 ))
}

confz_vserver_listconfigs_hook_check() {
	checkvars name

	(($+commands[vserver-listconfigs])) || \
		die "can't find vserver-listconfigs in \$PATH"

	[[ -x /etc/vservers/$vars[name]/scripts/post-start.d/vserver-listconfigs ]]
}

confz_vserver_listconfigs_hook_do() {
	local lnk
	lnk=/etc/vservers/$vars[name]/scripts/post-start.d/vserver-listconfigs
	mkdir -p $lnk:h || return $?
	ln -s $commands[vserver-listconfigs] $lnk || return $?
}


confz_vserver_autorestart_check() {
	checkvars name

	require vserver_listconfigs_hook :name
	require vserver_started :name %context_id

	do_command=( vserver -- $vars[name] condrestart )

	local -a files mtab vdir failures
	local fname mtab_list files_list

	vdir=/etc/vservers/$vars[name]/vdir
	vdir=$vdir:A
	mtab_list=/var/run/vservers/$vars[name].mtab
	files_list=/var/run/vservers/$vars[name].files

	if ! [[ -f $mtab_list ]]; then
		fail_reason="file $mtab_list not found"
		return 1
	fi

	if ! [[ -f $files_list ]]; then
		fail_reason="file $files_list not found"
		return 1
	fi

	mtab="$(grep '^[^ ]* '$vdir'[/ ]' /etc/mtab)"
	[[ $mtab == "$(</var/run/vservers/$vars[name].mtab)" ]] || \
		failures+=( "changes in mountpoints detected over ${(qqq)vdir}" )

	while IFS= read fname; do
		[[ -e $fname ]] || failures+=( "missing file: ${(qqq)fname}" )
		[[ $files_list -nt $fname ]] || failures+=(
			"${(qqq)fname} changed since vserver start"
		)
	done <$files_list

	fail_reason=${(j:, :)failures}
	return $#failures
}


confz_vserver_autorestart_default_check() {
	local mark

	for mark in /etc/vservers/*/apps/init/mark(N); do
		if [[ $(<$mark) == default ]]; then
			require vserver_autorestart name=$mark:h:h:h:t
		fi
	done
}