#!/bin/sh
#
# Copyright (c) 2022-2023, Jesús Daniel Colmenares Oviedo <DtxdF@disroot.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

. "${AJ_CONFIG}"
. "${LIBDIR}/load"

lib_load "${LIBDIR}/log"
lib_load "${LIBDIR}/check_func"
lib_load "${LIBDIR}/replace"
lib_load "${LIBDIR}/jail"

ESCAPE_CHARS='`"\$'
ENTRYPOINT=
WORKDIR="/"
USERNAME="root"

main()
{
	local args="$1"
	if lib_check_empty "${args}"; then
		lib_err - "usage: RUN [--maintain-env] [--noclean] [--juser <username>|--huser <username>]"
		lib_err - "               [<command> [<args> ...]]"
		exit ${EX_USAGE}
	fi

	# Entrypoint.
	get_entrypoint

	# Working directory.
	get_workdir

	# Username.
	get_username

	# Environment.
	get_environment

	# jexec options
	local maintain_env=0
	local noclean=0
	local juser=
	local huser=

	# Arguments
	local args_list
	local total_items
	local current_index=0

	args_list=`lib_split_jailparams "${args}"` || exit $?
	total_items=`printf "%s\n" "${args_list}" | wc -l`
	
	while [ ${current_index} -lt ${total_items} ]; do 
		current_index=$((current_index+1))
		local arg=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`
		if lib_check_empty "${arg}"; then
			continue
		fi

		local optarg=
		case "${arg}" in
			--juser|--huser)
				current_index=$((current_index+1))
				if [ ${current_index} -eq ${total_items} ]; then
					main # usage
				fi

				optarg=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`
				if lib_check_empty "${optarg}"; then
					main # usage
				fi
				;;
		esac

		case "${arg}" in
			--maintain-env)
				maintain_env=1
				;;
			--noclean)
				noclean=1
				;;
			--huser)
				huser="${optarg}"
				;;
			--juser)
				juser="${optarg}"
				;;
			--)
				break
				;;
			--*)
				main # usage
				;;
			*)
				break
				;;
		esac
	done
	current_index=$((current_index-1))

	local jexec_args=

	if [ ${noclean} -eq 1 ]; then
		jexec_args="-l"
	fi

	if [ -n "${juser}" ]; then
		local escape_juser=`lib_escape_string "${juser}" "" "${ESCAPE_CHARS}"`

		jexec_args="${jexec_args} -U \"${escape_juser}\""
	elif [ -n "${huser}" ]; then
		local escape_huser=`lib_escape_string "${huser}" "" "${ESCAPE_CHARS}"`

		jexec_args="${jexec_args} -u \"${escape_huser}\""
	fi

	local prg_args=

	local entrypoint_args=
	local entrypoint_items=
	local entrypoint_index=0

	if ! lib_check_empty "${ENTRYPOINT}"; then
		entrypoint_args=`lib_split_jailparams "${ENTRYPOINT}"` || exit $?
		entrypoint_items=`printf "%s\n" "${entrypoint_args}" | wc -l`
		entrypoint_index=0

		while [ ${entrypoint_index} -lt ${entrypoint_items} ]; do
			entrypoint_index=$((entrypoint_index+1))

			local cmd=`printf "%s\n" "${entrypoint_args}" | head -${entrypoint_index} | tail -n 1`
			if ! lib_check_empty "${cmd}"; then
				cmd=`lib_escape_string "${cmd}" 3 "${ESCAPE_CHARS}"`
			fi

			if [ -z "${prg_args}" ]; then
				prg_args="\\\"${cmd}\\\""
			else
				prg_args="${prg_args} \\\"${cmd}\\\""
			fi
		done
	fi

	while [ ${current_index} -lt ${total_items} ]; do 
		current_index=$((current_index+1))

		local cmd=`printf "%s\n" "${args_list}" | head -${current_index} | tail -n 1`

		if ! lib_check_empty "${cmd}"; then
			cmd=`lib_escape_string "${cmd}" 3 "${ESCAPE_CHARS}"`
		fi

		if [ -z "${prg_args}" ]; then
			prg_args="\\\"${cmd}\\\""
		else
			prg_args="${prg_args} \\\"${cmd}\\\""
		fi
	done

	if [ ${maintain_env} -eq 1 ]; then
		su_args="-m"
	else
		su_args="-l"
	fi

	local rundir="${MAKEJAIL_TEMPDIR}/rundir"
	if ! mkdir -p "${rundir}"; then
		lib_err ${EX_IOERR} "Error creating ${rundir}"
	fi

	dynamic_env_file="${rundir}/${MAKEJAIL_CURRENT_STAGE}.dynamic-env"

	if [ ! -f "${dynamic_env_file}" ]; then
		cat << "EOF"
__APPJAIL_RUN_ENVIRONMENT_FILE__=`lib_generate_tempfile` || exit $?

__APPJAIL_RUN_ENVIRONMENT_FILE_ESCAPED__=`lib_escape_string "${__APPJAIL_RUN_ENVIRONMENT_FILE__}"`

lib_atexit_add "rm -f \"${__APPJAIL_RUN_ENVIRONMENT_FILE_ESCAPED__}\""

env | grep -Ee '^__APPJAIL_RUN_ENVIRONMENT_VAR__[a-zA-Z_][a-zA-Z0-9_]*(=.*)?$' | sed -Ee 's/__APPJAIL_RUN_ENVIRONMENT_VAR__(.+)/\1/' | while IFS= read -r env_var; do
	name=`lib_jailparam_name "${env_var}" "="`
	value=`lib_jailparam_value "${env_var}" "="`
	if lib_check_empty "${value}"; then
		lib_err ${EX_DATAERR} -- "${name}: this environment variable requires a value."
	fi
	value=`"${SCRIPTSDIR}/escape-env-val.sh" "${value}"`

	env="${name}=${value}"

	printf "%s " "\"${env}\""
done > "${__APPJAIL_RUN_ENVIRONMENT_FILE__}"

__APPJAIL_RUN_RETURNCODE__=$?

if [ ${__APPJAIL_RUN_RETURNCODE__} -ne 0 ]; then
	exit ${__APPJAIL_RUN_RETURNCODE__}
fi

__APPJAIL_RUN_ENVIRONMENT_FILE_ESCAPED__=`lib_escape_string "${__APPJAIL_RUN_ENVIRONMENT_FILE__}" 3`

__APPJAIL_RUN_ENVIRONMENT__=`head -1 -- "${__APPJAIL_RUN_ENVIRONMENT_FILE__}"`
EOF

		if ! touch -- "${dynamic_env_file}"; then
			lib_err ${EX_IOERR} "Error creating ${dynamic_env_file}"
		fi
	fi

	echo "echo \"cd \\\"${WORKDIR}\\\"; env ${ENV_VARS} \${__APPJAIL_RUN_ENVIRONMENT__} ${prg_args}\" | \"\${APPJAIL_SCRIPT}\" cmd jexec \"\${APPJAIL_JAILNAME}\" ${jexec_args} su ${su_args} \"${USERNAME}\" -c \"sh -s\""
}

get_entrypoint()
{
	local entrypoint_file="${MAKEJAIL_TEMPDIR}/entrypoint/${MAKEJAIL_CURRENT_STAGE}"
	if [ -f "${entrypoint_file}" ]; then
		ENTRYPOINT=`head -1 -- "${entrypoint_file}"`
	fi
}

get_workdir()
{
	local workdir_file="${MAKEJAIL_TEMPDIR}/workdir/${MAKEJAIL_CURRENT_STAGE}"
	if [ -f "${workdir_file}" ]; then
		WORKDIR=`head -1 -- "${workdir_file}"`
		WORKDIR=`lib_escape_string "${WORKDIR}" 3 "${ESCAPE_CHARS}"`
	fi
}

get_username()
{
	local username_file="${MAKEJAIL_TEMPDIR}/user/${MAKEJAIL_CURRENT_STAGE}"
	if [ -f "${username_file}" ]; then
		USERNAME=`head -1 -- "${username_file}"`
		USERNAME=`lib_escape_string "${USERNAME}" "" "${ESCAPE_CHARS}"`
	fi
}

get_environment()
{
	local env_vars_file="${MAKEJAIL_TEMPDIR}/env/${MAKEJAIL_CURRENT_STAGE}"
	if [ -f "${env_vars_file}" ]; then
		ENV_VARS=`head -1 -- "${env_vars_file}"`
	elif [ -f "${env_vars_file}.tmp" ]; then
		ENV_VARS=`head -1 -- "${env_vars_file}.tmp"`
	fi
}

main "$@"
