#!/usr/local/bin/cbsd
# shellcheck shell=sh disable=2086,1090,2154,2166,3043,1091,2034
# v13.0.1

MYARG=""
MYOPTARG="jname dir remote shell src_session user"
MYDESC="Exec login into jail"
CBSDMODULE="jail"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

 Login info jail environment via login(1).
 If you are in the CBSDfile directory, these environments will always have priority
  user= login from the specified user. Default is: root

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}jname${N0_COLOR}   - target jail;
 ${N2_COLOR}dir${N0_COLOR}     - the working directory inside the jail.  The
                 default is the ~user directory.
 ${N2_COLOR}remote=${N0_COLOR} - '1' prevent to searching in remote node base. 
                 For the avoid the looping.
 ${N2_COLOR}shell${N0_COLOR}   - shell by default. Default is 'auto' ( try to determine it ourselves ),
                 or specify it in a fixed way, for example: '/bin/sh'
 ${N2_COLOR}user${N0_COLOR}    - login via another user. Default is 'root'.

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd jlogin
 # cbsd jlogin jname=jname1 user=nobody dir=/tmp shell=/bin/bash
 # cbsd jlogin jname=jname1 shell=zsh

${H3_COLOR}See also${N0_COLOR}:

 cbsd jexec --help

"
EXTHELP="wf_jlogin"

# Login by jid not supported yet

. ${subrdir}/nc.subr
cbsd_api=0
user=
ouser=
dir=
odir=
shell=
oshell=
. ${cbsdinit}
[ -n "${user}" ] && ouser="${user}"
[ -n "${dir}" ] && odir="${dir}"
[ -n "${shell}" ] && oshell="${shell}"
[ -z "${shell}" ] && shell="auto"
readconf jlogin.conf

try_remote()
{
	[ "${remote}" = "1" ] && err 1 "${N1_COLOR}No such jail${N0_COLOR}"
	RHST=$( jwhereis ${jname} | ${HEAD_CMD} -n1 )
	[ -z "${RHST}" ] && err 1 "No such jail for login"

	# we don't need for interactive when come with remote=2 from select_jail_by_list
	if [ "${remote}" != "2" -a "${always_rlogin}" = "0" ]; then
		getyesno "Found jail on remote node: ${RHST}. Try to login?"
		[ $? -eq 1 -o $? -eq 3 ] && err 1 "No such jail for login"
	fi

	rexe node=${RHST} /usr/local/bin/cbsd jlogin src_session=${nodename} jname=${jname} remote=1
	err 0 ""
}

init_tmux()
{
	if [ "${tmux_login}" != "1" -o -z "${src_session}" ]; then
		tmux_login=0
		return 0
	fi

	tmuxcmd=$( which tmux 2>/dev/null )

	src_session="$( ${WHOAMI_CMD} )-nodename"
	session_name="${src_session}-${jname}"

	if [ -n "${tmuxcmd}" ]; then
		# tmux here and we preferred them
		return 0
	fi

	# no tmux here
	tmux_login=0
	return 0
}


login_internal()
{
	local _res
	local _formfile

	#rctl/limits area
	. ${subrdir}/rctl.subr

	_formfile="${jailsysdir}/${jname}/helpers/jrctl.sqlite"
	[ -r "${_formfile}" ] && nice=$( cbsdsqlro ${_formfile} "SELECT cur FROM forms WHERE param='nice'" )
	[ -z "${nice}" ] && nice="0"

	if [ ${exec_fib} -eq 0 ]; then
		SETFIB=""
	else
		SETFIB="${SETFIB_CMD}  ${exec_fib}"
	fi

	if [ "${cpuset}" = "0" ]; then
		CPUSET=""
	else
		CPUSET="${CPUSET_CMD} ${cpuset}"
	fi

	if [ ! -d ${path}/bin ]; then
		err 1 "${N1_COLOR}Unknown environment, unable to login${N0_COLOR}"
	fi

	[ -n "${oshell}" ] && shell="${oshell}"
	[ -n "${ouser}" ] && user="${ouser}"
	[ -z "${user}" ] && user="root"
	[ -n "${oshell}" ] && shell="${oshell}"
	[ -z "${shell}" ] && shell="/bin/sh"

	if [ "${shell}" = "auto" ]; then
		if [ -r "${path}/etc/passwd" ]; then
			shell=$( ${miscdir}/getshell ${path}/etc/passwd ${user} )
			ret=$?
			if [ ${ret} -eq 0 ]; then
				true
				#${ECHO} "${N1_COLOR}getshell: shell='auto' -> ${N2_COLOR}shell=${shell}${N0_COLOR}"
			else
				${ECHO} "${N1_COLOR}getshell fallback: ${N2_COLOR}shell=${shell}${N0_COLOR}" 1>&2
			fi
			LOGIN_STR="${shell}"
		else
			# is linux?
			if [ -f "${path}/bin/bash" ]; then
				OSNAME=$( /usr/local/cbsd/misc/elf_tables --osname ${path}/bin/bash )
				LOGIN_STR="/bin/bash"
			elif [ -f "${path}/bin/sh" ]; then
				OSNAME=$( /usr/local/cbsd/misc/elf_tables --osname ${path}/bin/sh )
				LOGIN_STR="/bin/sh"
			elif [ -f ${path}/bin/busybox ]; then
				OSNAME=$( /usr/local/cbsd/misc/elf_tables --osname ${path}/bin/busybox )
				LOGIN_STR="/bin/sh"
			else
				err 1 "${N1_COLOR}Unknown environment, unable to login${N0_COLOR}"
			fi
		fi
	else
		LOGIN_STR="${shell}"
	fi

	case "${OSNAME}" in
		freebsd)
			if [ "${emulator}" != "jail" -a -n "${emulator}" ]; then
				. ${subrdir}/emulator.subr
				init_usermode_emul
				# inherit emulator_flags
				#LOGIN_STR="/bin/${emulator} ${LOGIN_CMD} -f ${user}"
				LOGIN_STR="/bin/${emulator} /bin/sh"
			fi
			;;
		*)
			true
			;;
	esac

	[ -n "${odir}" ] && dir="${odir}"
	[ -z "${dir}" ] && dir="~${user}"

	if [ "${platform}" = "DragonFly" ]; then
		# shellcheck disable=2153
		jexec="${NICE_CMD} -n ${nice} ${JEXEC_CMD} ${jid} ${LOGIN_STR}"
	else
		# old behavior
		# jexec="${NICE_CMD} -n ${nice} ${SETFIB} ${CPUSET} ${JEXEC_CMD} ${jid} ${LOGIN_STR}"

		# with 
		jexec="${NICE_CMD} -n ${nice} ${SETFIB} ${CPUSET} ${miscdir}/jexec_env ${jname} ${user} ${dir} ${LOGIN_STR}"
	fi
	init_tmux

	cbsdlogger NOTICE ${CBSD_APP}: executing jexec string for login: ${jexec}

	if [ ${tmux_login} -eq 1 ]; then
		${tmuxcmd} list-sessions | ${GREP_CMD} -qwF "${session_name}:"
		if [ $? -eq 1 ]; then
			${tmuxcmd} new -s "${session_name}" "eval ${jexec}"
			ret=$?
		else
			${tmuxcmd} attach-session -t "${session_name}"
			ret=$?
		fi
	else
		eval ${jexec}
		ret=$?
		if [ ${ret} -ne 0 ]; then
			${ECHO} "${N1_COLOR}${CBSD_APP} jexec errcode: ${ret}: ${jexec}${N0_COLOR}" 1>&2
		fi
	fi

	return ${ret}
}

login_custom()
{
	# reset PATH to default
	local CBSDPATH="${PATH}"
	# reset CBSD PATH
	export PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
	. ${subrdir}/jcreate.subr	# for export_jail_data_for_external_hook
	export_jail_data_for_external_hook
	readconf jlogin.conf
	${ECHO} "${N1_COLOR}Custom login command: ${N2_COLOR}${login_cmd}${N0_COLOR}"
	exec /bin/sh -c "${login_cmd}"
}

# jname must be set
jname_jlogin()
{
	. ${subrdir}/rcconf.subr
	[ $? -eq 1 ] && try_remote
	[ "${emulator}" = "bhyve" ] && err 1 "${N1_COLOR}for bhyve please use: ${N2_COLOR}cbsd blogin=${jname} ${N1_COLOR}instead${N0_COLOR}"
	[ "${baserw}" = "1" ] && path=${data}
	[ ${jid} -eq 0 ] && err 1 "not running"

	readconf jlogin.conf

	cbsdlogger NOTICE ${CBSD_APP}: login into ${jname}

	case "${login_cmd}" in
		internal)
			login_internal
			;;
		*)
			login_custom
			;;
	esac
}

if [ -z "${user}" ]; then
	user="root"
else
	shift
fi

emulator="jail"		# for jname_is_multiple

# check for cloud function when CBSDfile exist
Makefile="${CBSD_PWD}/CBSDfile"
if [ -r "${Makefile}" ]; then
	[ -z "${CBSDFILE_RECURSIVE}" ] && ${ECHO} "${N1_COLOR}found CBSDfile: ${N2_COLOR}${Makefile}${N0_COLOR}"
	. ${Makefile}

	all_jail_list=$( ${GREP_CMD} -E '^jail_[a-zA-Z0-9_@%:][-a-zA-Z0-9_@%:]*\(\)$' ${Makefile} | ${XARGS_CMD} | ${TR_CMD} -d "()" | ${SED_CMD} s#jail_##g )
	[ -z "${all_jail_list}" ] && err 1 "${N1_COLOR}${CBSD_APP}: no jail found${N0_COLOR}"
	[ -z "${jname}" ] && jname="${1}"

	if [ -n "${jname}" ]; then
		found=0
		for i in ${all_jail_list}; do
			if [ "${i}" = "${jname}" ]; then
				found=1
				break
			fi
		done
		[ ${found} -eq 0 ] && err 1 "${N1_COLOR}${CBSD_APP}: no such jail: ${N2_COLOR}${jname}${N0_COLOR}"
		all_jail_list="${jname}"
	fi

	all_jail_list_num=0
	for i in ${all_jail_list}; do
		all_jail_list_num=$(( all_jail_list_num + 1 ))
	done

	if [ -n "${CLOUD_URL}" -a -n "${CLOUD_KEY}" ]; then
		cbsd_api=1
	else
		# CBSDfile without API
		if [ ${all_jail_list_num} -gt 1 ]; then
			# select jails from CBSDfile scope
			jname=
			select_jail_by_list -s "List of online jails limited by CBSDfile scope [${all_jail_list}]:" -l "${all_jail_list}" -r 0 -e jls
			if [ -n "${jname}" ]; then
				${ECHO} "${N1_COLOR}exec jlogin to: ${N2_COLOR}${jname}${N0_COLOR}" 1>&2
				jname_jlogin
			fi
		else
			jname="${all_jail_list}"
			${ECHO} "${N1_COLOR}exec jlogin to: ${N2_COLOR}${jname}${N0_COLOR}" 1>&2
			jname_jlogin
		fi
		exit 0
	fi
else
	cbsd_api=0
fi

if [ ${cbsd_api} -eq 1 ]; then
	CURL_CMD=$( which curl )
	JQ_CMD=$( which jq )
	[ -z "${CURL_CMD}" ] && err 1 "${N1_COLOR}cloud up requires curl, please install: ${N2_COLOR}pkg install -y curl${N0_COLOR}"
	[ -z "${JQ_CMD}" ] && err 1 "${N1_COLOR}cloud up requires jq, please install: ${N2_COLOR}pkg install -y textproc/jq${N0_COLOR}"
	[ -z "${CBSDFILE_RECURSIVE}" ] && ${ECHO} "${N1_COLOR}main cloud api: ${N2_COLOR}${CLOUD_URL}${N0_COLOR}" 1>&2
	_cid=$( ${miscdir}/cbsd_md5 "${CLOUD_KEY}" )

	for jname in ${all_jail_list}; do
		[ -z "${jname}" ] && return 1
		_ssh=$( ${CURL_CMD} --no-progress-meter -H "cid:${_cid}" ${CLOUD_URL}/api/v1/status/${jname} 2>&1 )
		_ret=$?
		if [ ${_ret} -ne 0 ]; then
			${ECHO} "${N1_COLOR}${CBSD_APP} error: curl error1: ${N2_COLOR}${_ssh}${N0_COLOR}"
			${ECHO} "${CURL_CMD} --no-progress-meter -H \"cid:XXXXX\" ${CLOUD_URL}/api/v1/status/${jname}"
			continue
		fi
		_ssh_string=$( echo "${_ssh}" | ${JQ_CMD} '.ssh_string' | ${TR_CMD} -d '"' )
		_ssh_pref=$( substr --pos=0 --len=3 --str="${_ssh_string}" )
		if [ "${_ssh_pref}" != "ssh" ]; then
			${ECHO} "${N1_COLOR}${CBSD_APP} error: curl error2, no ssh_string:\n${N2_COLOR}${_ssh}${N0_COLOR}"
			${ECHO} "${CURL_CMD} --no-progress-meter -H \"cid:XXXXX\" ${CLOUD_URL}/api/v1/status/${jname}"
			continue
		fi
		_ssh_len=$( strlen "${_ssh_string}" )
		_ssh_post=$( substr --pos=5 --len=${_ssh_len} --str="${_ssh_string}" )
		#echo "${SSH_CMD} ${_ssh_post}"
		# rewrite
		if [ -n "${SUDO_USER}" ]; then
			if [ -r /home/${SUDO_USER}/.ssh/id_ed25519 ]; then
				_ssh_sudo_arg="-oIdentityFile=/home/${SUDO_USER}/.ssh/id_ed25519"
			elif [ -r /home/${SUDO_USER}/.ssh/id_rsa ]; then
				_ssh_sudo_arg="-oIdentityFile=/home/${SUDO_USER}/.ssh/id_rsa"
			else
				date
			fi
		fi
		jexec_cmd="${SSH_CMD} -tt -oStrictHostKeyChecking=no -oConnectTimeout=5 -oServerAliveInterval=10 ${_ssh_sudo_arg} ${_ssh_post}"
		echo "[debug] exec: ${jexec_cmd}"
		${jexec_cmd}
	done
	exit 0
fi

jname_is_multiple	# import jail_list if jname is mask

if [ -n "${jail_list}" ]; then
	${ECHO} "${N1_COLOR}Found jails by mask: ${N2_COLOR}${jail_list}${N0_COLOR}"
	for jname in ${jail_list}; do
		${ECHO} "${N1_COLOR}exec jlogin to: ${N2_COLOR}${jname}${N0_COLOR}"
		jlogin jname=${jname}
	done
	exit 0
fi

[ -z "${jname}" ] && jname=$1
[ -z "${jname}" ] && select_jail_by_list -s "List of online jail" -a "On" -r ${sqlreplica}
[ -z "${jname}" ] && err 1 "${N1_COLOR}Please specify jname as argument${N0_COLOR}"
[ -z "${remote}" ] && remote=0

validate_jname "${jname}" || err 1 "${N1_COLOR}Bad jname${N0_COLOR}"

jname_jlogin

exit 0
