#!/sbin/runscript
# Copyright 1999-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/www/viewcvs.gentoo.org/raw_cvs/gentoo-x86/sys-cluster/vzctl/files/vz-3.0.10.initd,v 1.2 2006/07/09 10:29:24 phreak Exp $

depend() {
	after net vzeventd
}

check_config() {
	# if we don't want openvz running, say so
	[ "${VIRTUOZZO}" = "yes" ] || return 1

	# set default values
	: ${VZCTL:=@SBINDIR@/vzctl}
	: ${VZQUOTA:=@SBINDIR@/vzquota}
	: ${VZVEINFO:=/proc/vz/veinfo}
	: ${VESTAT:=/proc/vz/vestat}
	: ${VPSCONFDIR:=@VPSCONFDIR@}
	: ${VZREBOOTDIR:=@VZREBOOTDIR@}
	: ${VZDEV:=venet0}
	: ${VZCONF:=@PKGCONFDIR@/vz.conf}
	: ${VE_STOP_MODE:=suspend}

	if [ "${MODULES_DISABLED}" != "yes" ]; then
		: ${IPTABLES_MODULES:="${IPTABLES}"}
		IPTABLES_MODULES="ip_tables ${IPTABLES_MODULES} xt_tcpudp"
		: ${PRELOAD_MODULES:="af_packet"}
		: ${MODULES:="vzmon vzdquota vzdev"}
		: ${MIGRATE_MODULES:="vzcpt vzrst"}
		: ${NET_MODULES="vznetdev vznet vzethdev"}
		: ${PLOOP_MODULES="ploop pfmt_ploop1 pfmt_raw pio_direct pio_nfs"}

		# check if you should load vzwdog module
		[ "${VZWDOG}" = "yes" ] && MODULES="${MODULES} vzwdog"
	fi

	# we need a working vzctl
	if [ ! -x "${VZCTL}" ]; then
		eerror "vzctl missing (${VZCTL})"
		return 1
	fi

	if [ -z "${VE0CPUUNITS}" ]; then
		ewarn "VE0CPUUNITS is not set in /etc/conf.d/vz; using default value of 1000"
		VE0CPUUNITS=1000
	fi
	return 0
}

mount_cgroups()
{
	local g
	for g in beancounter container fairsched ; do
		if [ -d /proc/vz/$g ]; then
			mount -t cgroup $g /proc/vz/$g -o name=$g 2>/dev/null
		fi
	done
}

umount_cgroups()
{
	local g
	for g in beancounter container fairsched ; do
		umount /proc/vz/$g 2>/dev/null
	done
}

setup_ve0() {
	local msg ve0conf="${VPSCONFDIR}/0.conf"

	mount_cgroups

	msg=$(${VZCTL} set 0 --cpuunits ${VE0CPUUNITS} 2>&1)
	[ $? -ne 0 ] && ewarn "vzctl set 0 --cpuunits ${VE0CPUUNITS} failed: ${msg}"

	test -f ${ve0conf} || return
	egrep -q '^ONBOOT=yes\|^ONBOOT=\"yes\"' ${ve0conf} || return

	ebegin "Configuring hardware node UB resources"
	msg=$(${VZCTL} set 0 --reset_ub 2>&1)
	eend $? ${msg}
}

start_net() {
	local mod

	# load necessary modules
	for mod in ${NET_MODULES}; do
		modprobe ${mod} 2>/dev/null
	done

	if [ ! -f ${VZVEINFO} ]; then
		return 0
	fi

	# we don't operate on a running interface
	if ip addr list | grep -q "venet0:.*UP" 2>/dev/null; then
		return 0
	fi

	# configure the device
	ebegin "Bringing up interface ${VZDEV}"
		ip link set ${VZDEV} up
	eend $?

	ip addr add 0.0.0.0/0 dev ${VZDEV}

	ebegin "Configuring interface ${VZDEV}"
		sysctl -q -w net.ipv4.conf.${VZDEV}.send_redirects=0
	eend $?

	if [ "x$(sysctl -n -e net.ipv4.ip_forward)" != "x1" ]; then
		ewarn "It looks like you have ip forwarding disabled. To make networking"
		ewarn "available for containers, please, run sysctl -w net.ipv4.ip_forward=1"
	fi
}

stop_net() {
	local mod

	if ip addr list | grep -q "venet0:.*UP" 2>/dev/null; then
		ebegin "Bringing down interface ${VZDEV}"
			ip link set ${VZDEV} down 2>/dev/null
		eend $?
	fi

	# remove all modules we probably loaded on start_net
	for mod in ${NET_MODULES}; do
		modprobe -r ${mod} > /dev/null 2>&1
	done
}

start_ve() {
	local veid velist msg need_restart=""

	# CTs that were running before a reboot
	velist=$(ls $VZREBOOTDIR)
	rm -f $VZREBOOTDIR/*
	# ... and not have ONBOOT=no
	test -n "$velist" && velist=$(vzlist -aH -octid,onboot $velist |
			awk '$2 != "no" {print $1}')
	# ... plus ones with ONBOOT=yes
	velist=$(echo "$velist"; vzlist -aH -octid,onboot |
			awk '$2 == "yes" {print $1}')
	# Then sort by bootorder
	test -n "$velist" && velist=$(vzlist -aH -octid -s-bootorder $velist)

	sysctl -q -w net.ipv4.route.src_check=0

	for veid in ${velist}; do
		ebegin "Starting CT ${veid}"
		if [ "${VZFASTBOOT}" = "yes" -a "${DISK_QUOTA}" = "yes" ]; then
			${VZQUOTA} stat ${veid} >/dev/null 2>&1
			if [ $? -eq 6 ]; then
				if ${VZQUOTA} show ${veid} 2>&1 | grep "vzquota : (warning) Quota is running" >/dev/null 2>&1; then
					${VZQUOTA} on ${veid} --nocheck >/dev/null 2>&1
					need_restart="${need_restart} ${veid}"
				fi
			fi
		fi
		rm -f $VZREBOOTDIR/$veid
		msg=$(${VZCTL} start ${veid} 2>&1)
		eend $? "${msg}"
	done

	for veid in ${need_restart}; do
		ebegin "Stopping CT ${veid}"
		msg=$(${VZCTL} stop ${veid})
		eend $? "${msg}"

		ebegin "Starting CT ${veid}"
		msg=$($VZCTL start ${veid} 2>&1)
		eend $? "${msg}"
	done

	# we're ok even if some CTs failed to start
	return 0
}

get_parallel()
{
	[ -n "${PARALLEL}" ] && return
	PARALLEL=`awk '
BEGIN { num=0; }
$1 == "processor" { num++; }
END { print num * 4; }' /proc/cpuinfo`
}

stop_ve() {
	local veid velist i iter pid pids stage stages msg

	if [ ! -f ${VESTAT} ]; then
		return
	fi

	# Pre-stop stage
	rm -f $VZREBOOTDIR/*
	velist=$(vzlist -1 2>/dev/null)
	for veid in $velist; do
		# Equalize cpuunits for all CTs
		$VZCTL set $veid --cpuunits 2000 >/dev/null 2>&1
		# Save to vzreboot list
		touch $VZREBOOTDIR/$veid
	done
	get_parallel
	stages="stop"
	[ "$VE_STOP_MODE" = "suspend" ] && stages="suspend stop"
	for stage in $stages; do
		case $stage in
			suspend)
				msg='Suspending CT'
				;;
			stop)
				msg='Shutting down CT'
				;;
		esac
		for ((i = 0; i <= 2; i++)); do
			iter=0
			pids=
			velist=$(vzlist -H -o ctid -sbootorder 2>/dev/null)
			for veid in ${velist}; do
				ebegin "$msg $veid"
				if [ "$stage" = "stop" ]; then
					# Unset cpulimit for CT to stop fast
					$VZCTL set $veid --cpulimit 0 >/dev/null 2>&1
				fi
				${VZCTL} --skiplock $stage ${veid} >/dev/null 2>&1 &
				pids="${pids} $!"
				let iter++
				if [ ${iter} -gt ${PARALLEL} ]; then
					for pid in ${pids}; do
						wait ${pid}
					done
					pids=
					iter=0
				fi
			done

			for pid in ${pids}; do
				wait ${pid}
			done
		done
	done
}

umount_ve() {
	local iter=0
	local fail=1
	local m mounts msg quota

	while [ ${iter} -lt 5 -a ${fail} -ne 0 ]; do
		fail=0
		mounts=$(awk '{if ($3=="simfs") print $2}' /proc/mounts)

		for m in ${mounts}; do
			ebegin "Unmounting CT area ${m}"
			msg=$(umount ${m} 2>&1)
			eend $? "${msg}"
			if [ $? -ne 0 ]; then
				let fail++
				fuser -k -m ${m} > /dev/null 2>&1
			fi
		done

		let iter++
	done

	# turn quota off
	quota=$(awk -F: '/^[0-9]+:/{print $1}' /proc/vz/vzquota 2>/dev/null)

	for m in ${quota}; do
		ebegin "Turning quota off for CT ${m}"
		msg=$(vzquota off ${m} 2>&1)
		eend $? "${msg}"
	done
}

modify_vzconf()
{
	# For vswap-enabled kernel (like RHEL6 042test or greater),
	# use vswap config for CT. Don't do it if CONFIGFILE= is modified

	egrep -q 'test|stab' /proc/vz/version 2>/dev/null || return 0

	local cfg_old='^CONFIGFILE="basic" # Use vswap-256m on RHEL6 kernel$'
	local cfg_new='CONFIGFILE="vswap-256m"'
	local kv=$(cat /proc/vz/version | sed 's/\(test\|stab\).*$//')
	if test $kv -ge 42; then
		if grep -q "$cfg_old" $VZCONF; then
			sed -i "s/${cfg_old}/${cfg_new}/" $VZCONF
			ewarn "Setting $cfg_new in $VZCONF"
		fi
	fi
}


start() {
	check_config || return

	local mod rc

	ebegin "Loading OpenVZ modules"
	for mod in ${IPTABLES_MODULES}; do
		modprobe ${mod} >/dev/null 2>&1
	done

	for mod in ${PRELOAD_MODULES}; do
		modprobe -r ${mod} >/dev/null 2>&1
		modprobe ${mod} >/dev/null 2>&1
	done

	for mod in ${MODULES}; do
		modprobe ${mod} >/dev/null 2>&1
		rc=$?
		if [ ${rc} -ne 0 ]; then
			eend ${rc} "failed to load module ${mod}"
			return ${rc}
		fi
	done

	for mod in ${MIGRATE_MODULES} ${PLOOP_MODULES}; do
		modprobe ${mod} >/dev/null 2>&1
	done
	eend

	if [ ! -e /dev/vzctl ]; then
		eerror "Missing device node /dev/vzctl"
		einfo
		einfo "Please create the vzctl device node using the following command:"
		einfo "   /bin/mknod /dev/vzctl c 126 0"
		einfo
		return 1
	fi

	if [ -f /proc/vz/oom_score_adj ]; then
		ebegin "Applying OOM adjustments"
		cat /etc/vz/oom-groups.conf > /proc/vz/oom_score_adj
		eend $?
	fi

	start_net
	setup_ve0
	modify_vzconf
	start_ve
	# Try to run vzstats to submit new kernel info
	vzstats >/dev/null 2>&1
}

stop() {
	check_config || return

	# Avoid stop action inside a CT, check we are in CT0
	if test -r /proc/user_beancounters; then
		if ! egrep -q '^[[:space:]]*0:[[:space:]]' \
				/proc/user_beancounters; then
			eerror "Looks like we are inside a container!"
			return 1
		fi
	fi

	local mod

	stop_ve
	umount_ve
	umount_cgroups
	stop_net

	for mod in ${MIGRATE_MODULES} ${PLOOP_MODULES}; do
		modprobe -r ${mod} > /dev/null 2>&1
	done

	for mod in ${MODULES}; do
		modprobe -r ${mod} > /dev/null 2>&1
	done

	for mod in ${PRELOAD_MODULES}; do
		modprobe -r ${mod} > /dev/null 2>&1
	done

	# Even if some modules failed to unload (say they were not loaded)
	# we return 0 for the service to be marked as stopped.
	return 0
}
