#!/bin/sh
#  Copyright (c) 2013-2017, Parallels International GmbH
# Copyright (c) 2017-2019 Virtuozzo International GmbH. All rights reserved.
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#  Our contact details: Virtuozzo International GmbH, Vordergasse 59, 8200
#  Schaffhausen, Switzerland.
#
# This script is called by CRIU (http://criu.org) after creating namespaces.
#
# Parameters are passed in environment variables.
# Required parameters:
#   STATUSFD	  - file descriptor for sending signal to vzctl
#   WAITFD	  - file descriptor for receiving signal from vzctl
#   CRTOOLS_SCRIPT_ACTION - current action (set by criu)

exec 1>&2

action_script=/usr/libexec/libvzctl/scripts/vz-rst-action

update_devices()
{
	local olddev=$1
	local newdev=$2
	local major=$3
	local minor=$4
	local omj omr mj mr
	local dir=${olddev%/*}
	local newdir=${newdev%/*}

	rm -f $newdev
	[ -d $newdir ] || mkdir -p $newdir
	mknod -m 660 $newdev b $major $minor
	[ -e "$olddev" ] || return

	omj=$(stat -c %t $olddev)
	omj=$(printf "%d" 0x$omj)
	omr=$(stat -c %T $olddev)
	omr=$(printf "%d" 0x$omr)
	for n in `ls -1 $dir`; do
		mj=$(stat -c %t $dir/$n)
		mj=$(printf "%d" 0x$mj)

		mr=$(stat -c %T $dir/$n)
		mr=$(printf "%d" 0x$mr)

		if [ "$mj" = "$omj" -a "$mr" = "$omr" ]; then
			rm -f $dir/$n
			mknod -m 660 $dir/$n b $major $minor
		fi
	done
}

restore_devices()
{
	local s d t major minor old dir device
	local pid=$CRTOOLS_INIT_PID

	if [ -z "$VZ_RST_USE_NSENTER" ]; then
		export VZ_RST_USE_NSENTER="y"
		nsenter -m -t $pid bash $action_script
		exit 0
	fi

	if [ -n "$CRIU_DEVTMPFS" ]; then
		MNT=$CRIU_DEVTMPFS
	else
		MNT=$CRIU_MNT_NS_ROOTS
	fi
	for root in $MNT; do
		# VE_PLOOP_DEVS=UUID@ploopN:major:minor:[root]
		for s in $VE_PLOOP_DEVS; do
			uuid=${s%%@*}
			t=${s#*@}
			device=${t%%:*}
			[ -n "$CRIU_DEVTMPFS" ] &&  device=${device#/dev}
			t=${t#*:}
			major=${t%%:*}
			t=${t#*:}
			minor=${t%%:*}

			[ ! -L "$root/$uuid" ] && continue

			old=$(readlink $root/$uuid)
			[ -z "$old" ] && continue
			[ -n "$CRIU_DEVTMPFS" ] && old=${old#/dev}

			update_devices "$root/$pxf/$old" "$root/$device" $major $minor
			rm -f $root/dev/$uuid
		done
	done
}

if [ -z "$CRTOOLS_SCRIPT_ACTION" ]; then
	echo "Missing parameter CRTOOLS_SCRIPT_ACTION"
	exit 1
fi

set -e
case "$CRTOOLS_SCRIPT_ACTION" in
"setup-namespaces")
	pid=$CRTOOLS_INIT_PID
	ln -s /proc/$pid/ns/net $VE_NETNS_FILE

	if [ -n "$VEID" ]; then
		[ -n "$VE_CLOCK_BOOTBASED" ] && cgset -r ve.clock_bootbased="$VE_CLOCK_BOOTBASED" $VEID
		[ -n "$VE_CLOCK_MONOTONIC" ] && cgset -r ve.clock_monotonic="$VE_CLOCK_MONOTONIC" $VEID
		[ -n "$VE_IPTABLES_MASK" ] && cgset -r ve.iptables_mask="$VE_IPTABLES_MASK" $VEID
		[ -n "$VE_FEATURES" ] && cgset -r ve.features="$VE_FEATURES" $VEID
		[ -n "$VE_AIO_MAX_NR" ] && cgset -r ve.aio_max_nr="$VE_AIO_MAX_NR" $VEID
	fi
	;;
"post-setup-namespaces")
	if [ -n "$VEID" ]; then
		restore_devices
		[ -n "$VE_OS_RELEASE" ] && cgset -r ve.os_release="$VE_OS_RELEASE" $VEID
		[ -n "$VE_PID_MAX" ] && cgset -r ve.pid_max="$VE_PID_MAX" $VEID
	fi
	;;
"pre-resume")
	#
	# We drop pseudosuper on pre-resume stage since we need to run
	# iptable helpers inside container during sockets restore. Note
	# the containers are still in stopped stage, CRIU will kick them
	# up to run via freezer after this stage.
	[ -n "$VEID" ] && { cgset -r ve.pseudosuper="0" $VEID || { echo "Failed to drop pseudosuper on $VEID"; exit 1; } }
	;;
"post-restore")
	if [ -z "$CRTOOLS_IMAGE_DIR" ]; then
		echo "Missing parameter CRTOOLS_IMAGE_DIR"
		exit 1
	fi

	if [ -n "$VEID" ]; then
		[ -f "$CRTOOLS_IMAGE_DIR/vz_core_pattern.img" ] && \
			{ cgexec -g ve:$VEID echo `cat $CRTOOLS_IMAGE_DIR/vz_core_pattern.img` \
			 > /proc/sys/kernel/core_pattern || { echo "Failed to restore core_pattern"; exit 1; } }
		if [ -f "/proc/sys/fs/fsync-enable" ] && [ -f "$CRTOOLS_IMAGE_DIR/vz_fsync-enable.img" ]; then
			cgexec -g ve:$VEID echo `cat $CRTOOLS_IMAGE_DIR/vz_fsync-enable.img` \
				> /proc/sys/fs/fsync-enable || { echo "Failed to restore fsync-enable"; exit 1; }
		fi
		[ -f "$CRTOOLS_IMAGE_DIR/vz_odirect_enable.img" ] && \
			{ cgexec -g ve:$VEID echo `cat $CRTOOLS_IMAGE_DIR/vz_odirect_enable.img` \
			 > /proc/sys/fs/odirect_enable || { echo "Failed to restore odirect_enable"; exit 1; } }
		[ -f "$CRTOOLS_IMAGE_DIR/vz_randomize_va_space.img" ] && \
			{ cgexec -g ve:$VEID echo `cat $CRTOOLS_IMAGE_DIR/vz_randomize_va_space.img` \
			 > /proc/sys/kernel/randomize_va_space || { echo "Failed to restore randomize_va_space"; exit 1; } }
	fi

	ret=0
	[ -n "$CRIU_ACTION_POST_RESUME_READ_FD" ] && printf '\0\0\0\0' >&${CRIU_ACTION_POST_RESUME_READ_FD}
	[ -n "$CRIU_ACTION_POST_RESUME_WRITE_FD" ] && ret=$(head -c 4 <&${CRIU_ACTION_POST_RESUME_WRITE_FD} | hexdump -e '"%d"' -n 4)
	[ "$ret" = "0" ] || { echo "Failed on action script in post-restore for $VEID"; exit 1; }

	ret=0
	[ -n "$STATUSFD" ] && printf '\0\0\0\0' >&${STATUSFD}
	[ -n "$WAITFD" ] && ret=$(head -c 4 <&$WAITFD | hexdump -e '"%d"' -n 4)
	[ "$ret" = "0" ] || { echo "Failed on post-restore for $VEID"; exit 1; }
	;;
esac

#
# Exit with success by default, any error must cause
# messaging with explicit "exit 1"
exit 0
