#!/bin/bash
#  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.
#
# Checkpoint a container using CRIU (http://criu.org).
# Useful with recent upstream (i.e. non-OpenVZ) kernels.
# Requires criu to be installed.
#
# Parameters are passed in environment variables.
# Required parameters:
#   VE_ROOT     - container root directory
#   VE_DUMP_DIR - directory for saving dump files
#   VE_PID      - PID of CT init process
exec 1>&2

action_script=/usr/libexec/libvzctl/scripts/vz-cpt-action
dumpdir="$VE_DUMP_DIR".tmp
ext_mount_map=
for s in $VE_CGROUP_MOUNT_MAP; do
	ext_mount_map="$ext_mount_map --ext-mount-map $s"
done

for s in $VE_EXT_MOUNT_MAP; do
	ext_mount_map="$ext_mount_map --ext-mount-map $s"
done

external= 
# VE_PLOOP_DEVS=UUID@ploopN:major:minor:[root]
for s in $VE_PLOOP_DEVS; do
	uuid=${s%%@*}
	t=${s#*@}
	device=${t%%:*}
	t=${t#*:}
	major=${t%%:*}
	t=${t#*:}
	minor=${t%%:*}
	root=${t#*:}

	[ "$root" = "root" ] && continue

	external="$external --external dev[$major/$minor]:$uuid"
done

[ -d $dumpdir ] && rm -rf $dumpdir

function cg_dump_props {
	if [ -n "$VEID" ]; then
		# Save monotonic offsets for next restore
		cgget -n -v -r ve.clock_bootbased $VEID > $1/vz_clock_bootbased.img
		cgget -n -v -r ve.clock_monotonic $VEID > $1/vz_clock_monotonic.img
		# Netfilter mask
		cgget -n -v -r ve.iptables_mask $VEID > $1/vz_iptables_mask.img
		# OS version virtualization
		cgget -n -v -r ve.os_release $VEID > $1/vz_os_release.img
		# Features granted to VE
		cgget -n -v -r ve.features $VEID > $1/vz_features.img
		# AIO statistics
		cgget -n -v -r ve.aio_max_nr $VEID > $1/vz_aio_max_nr.img
		# Maximum pid in VE
		cgget -n -v -r ve.pid_max $VEID > $1/vz_pid_max.img
	fi
	return 0
}

# NFS actions
nfs_actions_path=/usr/libexec/criu/scripts/nfs-ports-allow.sh
if [ -f "$nfs_actions_path" ]; then
	nfs_actions="--action-script $nfs_actions_path"
fi

mkdir -p $dumpdir &&
cg_dump_props $dumpdir &&

#
# There might be a number of other controllers created
# on the host system, so specify which ones are to
# be checkpointed.
cg_controllers="--cgroup-dump-controller hugetlb \
 --cgroup-dump-controller perf_event \
 --cgroup-dump-controller net_cls \
 --cgroup-dump-controller net_prio,net_cls \
 --cgroup-dump-controller pids \
 --cgroup-dump-controller freezer \
 --cgroup-dump-controller ve \
 --cgroup-dump-controller devices \
 --cgroup-dump-controller name=systemd \
 --cgroup-dump-controller cpuset \
 --cgroup-dump-controller cpuacct,cpu \
 --cgroup-dump-controller beancounter \
 --cgroup-dump-controller memory \
 --cgroup-dump-controller blkio"

CRIU_LOGLEVEL=4
criu dump -v$CRIU_LOGLEVEL -o dump.log				\
		--skip-in-flight			\
		--file-locks				\
		--tcp-established			\
		--evasive-devices			\
		--manage-cgroups			\
		--link-remap				\
		--root $VE_ROOT				\
		--freeze-cgroup $VE_FREEZE_CG		\
		--timeout 180				\
		--ghost-limit 4G			\
		--action-script $action_script		\
		-t $VE_PID				\
		-D $dumpdir				\
		$cg_controllers				\
		$nfs_actions				\
		$ext_mount_map				\
		$external				\
		$CRIU_EXTRA_ARGS

if [ $? -ne 0 ]; then
        [ -d $VE_DUMP_DIR.fail ] && rm -rf $VE_DUMP_DIR.fail
	grep ' Error ' $dumpdir/dump.log >&2
        mv -f $dumpdir $VE_DUMP_DIR.fail
        echo Failed to checkpoint the Container
        echo All dump files and logs were saved to $VE_DUMP_DIR.fail
        exit 1
else
	[ -d $VE_DUMP_DIR ] && rm -rf $VE_DUMP_DIR
	mv $dumpdir $VE_DUMP_DIR
	if [ $? -ne 0 ]; then
		echo "Failed to mv $dumpdir"
		exit 1
	fi
        echo Checkpointing finished successfully
fi
