#!/bin/sh

VETHNAME=""

log() {
	echo "${@}"|logger -t crun.runner -p info
	echo "${@}"
}

check_binary() {
	if [ -z "${1}" ]; then
		return 0;
	fi

	if ! command -v "${1}" >/dev/null 2>&1;then
		log "${1} not present in system"
		exit 1
	fi
	return 0;
}

check_binary crun
check_binary script

get_veth_name() {
	local name

	name="cr$1"
	VETHNAME="${name:0:15}"
}

clean_container_network() {
	local name

	name="${1}"
	# clean up
	if ip netns list|grep -qw "${name}"; then
		get_veth_name "${name}"
		ip netns del "${name}"
		ip link delete "${VETHNAME}" type veth peer name eth0
	fi
}

setup_container_network() {
	local name bridge_name

	if [ "$#" -lt 2 ]; then
		return 0
	fi

	name="${1}"
	bridge_name="${2}"

	clean_container_network "${name}"
	get_veth_name "${name}"
	# Add a namespace
	ip netns add "${name}"
	# Add a veth pair
	ip link add "${VETHNAME}" type veth peer name eth0 netns "$name"
	# Make veth on network side up
	ip netns exec "$name" ip link set dev eth0 up
	# Create loopback interface
	ip netns exec "$name" ip link set lo up
	# Make veth on host side up
	ip link set dev "${VETHNAME}" up
	# Link with host bridge
	brctl addif "${bridge_name}" "${VETHNAME}"
	# Get Ip from bridge and make the link ready
	ip netns exec "$name" udhcpc -i eth0 -x hostname:"CRUN-${name}"
	if [ "${du_status}" != "Installed" ]; then
		return 0;
	fi

}

run_container() {
	local bundle name bridge network

	bundle="${1}"
	name="${2}"
	bridge="${3}"

	if [ ! -d "${bundle:?}/${name:?}" ]; then
		log "Bundle does not exist"
		return 1
	fi

	# Only do the network setup if defined in config
	network="$(cat ${BUNDLE}/${NAME}/config.json |jsonfilter -e '@.linux.namespaces[@.type="network"]')"
	if [ -n "${network}" ] ; then
		setup_container_network "${name}" "${bridge}"
	else
		log "Network not defined in config, using host network..."
	fi
	
        script -q -c "crun run -b ${bundle}/${name} ${name}" /dev/null
}

update_network_ns() {
    local type
    json_select $2
    json_get_var type type
    if [ "${type}" = "network" ]; then
        json_add_string path "/var/run/netns/${NAME}"
	break;
    fi
    json_select ..
}

nginx_hack() {
	# replace nginx user to root user
	if [ -f "${BUNDLE:?}/${NAME:?}/rootfs/etc/nginx/nginx.conf" ]; then
		sed -i 's/^user .*;$/user root;/' "${BUNDLE}/${NAME}/rootfs/etc/nginx/nginx.conf"
	fi
}

update_config_json() {

	if [ ! -f "${BUNDLE:?}/${NAME:?}/config.json" ]; then
		log "config.json not found or bundle missing"
		exit 0;
	fi

	if [ -f "/usr/share/libubox/jshn.sh" ]; then
		. /usr/share/libubox/jshn.sh
	else
		log "jshn.sh missing in the system"
		exit 1;
	fi
	cd "${BUNDLE}/${NAME}"
	if cat config.json |jsonfilter -e '@.linux.namespaces[@.type="network"].path' |grep -q ${NAME}; then
		# If netns already configured and no additional permission bit assigned, exit from here
		if [ -z "${PERM}" ]; then
			exit 0;
		fi
	fi

	mv config.json config_orig.json
	json_init
	json_load_file "config_orig.json"

	# update hostname to container name
	if [ -n "${NAME}" ]; then
		json_add_string hostname "${NAME}"
	fi

	if [ -n "${ENVLIST}" ]; then
		json_select process
		json_get_values preenv env
		json_select env
		for i in ${ENVLIST//;/ }; do
			key="$(echo $i|cut -d= -f1)"
			if [[ "${preenv}" != *"${key}="* ]]; then
				json_add_string "" "${i}"
			fi
		done
		json_select ..
		json_select ..
	fi

	# Update cabalities
	if [ -n "${PERM}" ]; then
		log "Updating Permission in the json ..."
		PERM="${PERM//,/ }"
		json_select process
			json_select capabilities
				json_select bounding
					for p in ${PERM}; do
						json_add_string "" ${p}
					done
				json_select ..
				json_select effective
					for p in ${PERM}; do
						json_add_string "" ${p}
					done
				json_select ..
				json_select inheritable
					for p in ${PERM}; do
						json_add_string "" ${p}
					done
				json_select ..
				json_select permitted
					for p in ${PERM}; do
						json_add_string "" ${p}
					done
				json_select ..
				json_select ambient
					for p in ${PERM}; do
						json_add_string "" ${p}
					done
				json_select ..
			json_select ..
		json_select ..
	fi

	# update additional capabilities
	json_select linux
	json_for_each_item update_network_ns namespaces
	json_dump >config.json

	nginx_hack
}

pull_image_from_registry() {
	local temp disksize exsize

	if [ -z "${BUNDLE}" ] || [ -z "${NAME}" ] || [ -z "${REGURL}" ]; then
		log "Information missing for installation"
		exit 1
	fi

	if [ -d "${BUNDLE}/${NAME}" ]; then
		log "Container with same name already present"
		exit 1
	fi

	check_binary skopeo
	check_binary umoci
	temp="$(mktemp -d)"
	if [ ! -d "${temp}" ]; then
		log "Failed to create temp directory"
		exit 1
	fi

	cd "${temp}"
	OPTS=""
	INSPECT_OPT=""
	if [ "${#LOGIN}" -gt 3 ]; then
		OPTS="--src-creds ${LOGIN}"
		INSPECT_OPT="--creds ${LOGIN}"
	fi

	if ! skopeo --command-timeout ${TIMEOUT:-10}m copy ${OPTS} ${REGURL} oci:${NAME}_tmp:latest >/dev/null 2>&1; then
		log "Failed to download image: $?"
		cd -
		rm -rf "${temp}"
		exit 1
	fi
	if ! umoci unpack --image ${NAME}_tmp:latest ${NAME} >/dev/null 2>&1; then
		log "Failed to unpack image: $?"
		cd -
		rm -rf "${temp}"
		exit 1
	fi
	rm -rf "${NAME}_tmp"
	if [ ! -f "${NAME}/config.json" ]; then
		log "Failed to pull the image config missing"
		cd -
		rm -rf "${temp}"
		exit 1
	fi

	# Check disk available space before copy
	disksize=$(df -P -k ${BUNDLE} |tail -n +2 |awk '{print $4}')
	exsize=$(du -s ${temp} |awk '{print $1}')
	if [ "${disksize}" -lt "${exsize}" ]; then
		log "Disk space ${disksize} less than required ${exsize}"
		cd -
		rm -rf "${temp}"
		exit 1
	fi

	mv ${NAME} ${BUNDLE}/
	if [ "$?" -ne 0 ]; then
		log "Failed ${name} move in ${BUNDLE}"
		cd -
		rm -rf "${temp}"
		rm -rf ${BUNDLE}/${NAME}
		exit 1
	fi

	cd -
	rm -rf "${temp}"
	update_config_json
}

clean=0
net_update=0
update_json=0
PERM=""
ENVLIST=""

log "## Runner [$@] ##"

while getopts b:n:i:r:l:t:p:e:cuU options
do
	case "${options}" in
		b) BUNDLE=${OPTARG};;
		c) clean=1;;
		i) BRIDGE=${OPTARG};;
		n) NAME=${OPTARG};;
		p) PERM="${OPTARG}";;
		r) REGURL=${OPTARG};;
		l) LOGIN=${OPTARG};;
		t) TIMEOUT=${OPTARG};;
		e) ENVLIST=${OPTARG};;
		u) net_update=1;;
		U) update_json=1;;
		*) log "Invalid options";;
	esac
done

if [ -z "${NAME}" ]; then
	log "Emtpy container name"
	return 0;
fi

if [ "${update_json}" -eq "1" ]; then
	update_config_json
	return 0;
fi

if [ -n "${REGURL}" ]; then
	pull_image_from_registry
	return 0;
fi

if [ "$clean" -eq "1" ]; then
	clean_container_network "${NAME}"
	return 0;
fi

if [ -z "${BRIDGE}" ]; then
	log "Empty bridge name"
	return 0;
fi

if [ "${net_update}" -eq "1" ]; then
	get_veth_name "${NAME}"
	brctl addif "${BRIDGE}" "${VETHNAME}"
	return 0;
fi

if [ -n "${BUNDLE}" ] ; then
	run_container "${BUNDLE}" "${NAME}" "${BRIDGE}"
fi
