#!/usr/local/bin/cbsd
# fetch http://pkgmir.pkg.freebsd.org/freebsd:11:x86:64/latest/packagesite.txz
#v11.2.0
CBSDMODULE="jail"
MYARG="mode"
MYOPTARG="jname pkglist name repodir pkgconf chroot nomount cbsd_pkg_verbose cbsd_pkg_retry cbsd_pkg_interrupt_on_error cbsd_pkg_bootstrap_interrupt_on_error"
MYDESC="Manage jail packages via pkg(7)"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

 CBSD pkg - is wrapper around standart FreeBSD pkg(7) tools for more comfort work with the 
 jail packages from the master environment.

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}jname=${N0_COLOR}   - name of the jail to work, can be mask, e.g: '*', 'jail*';
 ${N2_COLOR}pkglist=${N0_COLOR} - <path>, read package list from file;
 ${N2_COLOR}name=${N0_COLOR}    - name of packages, e.g.: 'bash' or 'shells/bash';
 ${N2_COLOR}repodir=${N0_COLOR} - <path>, repository configuration directory;
 ${N2_COLOR}pkgconf=${N0_COLOR} - <path>, specify pkg.conf to use for pkg;
 ${N2_COLOR}nomount=${N0_COLOR} - when '1': do not mount/unmount jail FS, default: '0';
 ${N2_COLOR}mode=${N0_COLOR}    - action, can be:
   add       - alias for 'install';
   bootstrap - pass 'bootstrap' to jailed pkg, see man 'pkg';
   install   - pass 'install' to jailed pkg, see man 'pkg';
   info      - pass 'info' to jailed pkg, see man 'pkg';
   query     - pass 'query' to jailed pkg, see man 'pkg';
   remove    - pass 'remove' to jailed pkg, see man 'pkg';
   update    - pass 'update' to jailed pkg, see man 'pkg';
   upgrade   - pass 'upgrade' to jailed pkg, see man 'pkg';
   clean     - pass 'clean' to jailed pkg, see man 'pkg';
 ${N2_COLOR}cbsd_pkg_retry=${N0_COLOR}                        - overwrite number of retry when pkg failed (def: '2');
 ${N2_COLOR}cbsd_pkg_interrupt_on_error=${N0_COLOR}           - regulate behavior/errcode upon pkg failed (def: '1');
 ${N2_COLOR}cbsd_pkg_bootstrap_interrupt_on_error=${N0_COLOR} - regulate behavior upon pkg bootstrap failed (def: '0');

${H3_COLOR}Examples${N0_COLOR}:

 # cbsd pkg mode=update jname='*'
 # cbsd pkg mode=upgrade jname='redis*'
 # cbsd pkg mode=clean jname='*'
 # cbsd pkg mode=query jname='jail*' %o
 # cbsd pkg mode=install jname=mytest1 bash mc wget
 # cbsd pkg jname='jail*' mode=install nginx-devel mysql57-server postgresql13-server mc
 # cbsd pkg mode=remove jname=box1 wget lsof
 # cbsd pkg jname='jail*' mode=remove mc

"
EXTHELP="modules/pkg.d"

. ${subrdir}/nc.subr
cloud_api=0
. ${cbsdinit}

# store values from args to overwrite config
[ -n "${cbsd_pkg_retry}" ] && ocbsd_pkg_retry="${cbsd_pkg_retry}"
[ -n "${cbsd_pkg_interrupt_on_error}" ] && ocbsd_pkg_interrupt_on_error="${cbsd_pkg_interrupt_on_error}"
[ -n "${cbsd_pkg_bootstrap_interrupt_on_error}" ] && ocbsd_pkg_bootstrap_interrupt_on_error="${cbsd_pkg_bootstrap_interrupt_on_error}"
[ -n "${cbsd_pkg_verbose}" ] && ocbsd_pkg_verbose="${cbsd_pkg_verbose=0}"

. ${system}
#defines
_MYDIR=$( ${DIRNAME_CMD} `${REALPATH_CMD} $0` )
noname=0

set -e
	. ${_MYDIR}/pkg.subr
set +e

# read CBSD pkg defaults
readconf pkg.conf

# always force to .pkg extension (for old pkg)
[ -z "${PKG_SUFX}" ] && export PKG_SUFX=pkg

# init defaults when pkg.conf is absent
# see: /usr/local/cbsd/modules/pkg.d/etc-sample/pkg.conf for detauls
[ -z "${cbsd_pkg_retry}" ] && cbsd_pkg_retry=2
[ -z "${cbsd_pkg_interrupt_on_error}" ] && cbsd_pkg_interrupt_on_error=1
[ -z "${cbsd_pkg_bootstrap_interrupt_on_error}" ] && cbsd_pkg_bootstrap_interrupt_on_error=0
[ -z "${cbsd_pkg_verbose}" ] && cbsd_pkg_verbose=0

# restore args values if they set
[ -n "${ocbsd_pkg_retry}" ] && cbsd_pkg_retry="${ocbsd_pkg_retry}"
[ -n "${ocbsd_pkg_interrupt_on_error}" ] && cbsd_pkg_interrupt_on_error="${ocbsd_pkg_interrupt_on_error}"
[ -n "${ocbsd_pkg_bootstrap_interrupt_on_error}" ] && cbsd_pkg_bootstrap_interrupt_on_error="${ocbsd_pkg_bootstrap_interrupt_on_error}"
[ -n "${ocbsd_pkg_verbose}" ] && cbsd_pkg_verbose="${ocbsd_pkg_verbose=0}"

if [ -n "${nomount}" ]; then
	shift
	nomount=${nomount}
else
	nomount=0
fi

PKGARGS=""
TRAP=""
# for chroot/jail env
PKG_PRE=

if [ -z "${jname}" -a -n "${ojname}" ]; then
	# inherit jname env
	jname="${ojname}"
fi

# check for cloud function when CBSDfile exist
Makefile="${CBSD_PWD}/CBSDfile"
if [ -r "${Makefile}" -a "${CBSDFILE_RECURSIVE}" != "1" ]; 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 )

	if [ -n "${CLOUD_URL}" -a -n "${CLOUD_KEY}" ]; then
		cbsd_api=1
	else
		cbsd_api=0
	fi

	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}"
	fi
else
	cbsd_api=0
fi

if [ ${cbsd_api} -eq 1 ]; then
	CURL_CMD=$( which curl )
	[ -z "${CURL_CMD}" ] && err 1 "${N1_COLOR}cloud up requires curl, please install: ${N2_COLOR}pkg install -y curl${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
		_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 status error: ${N2_COLOR}${_ssh}${N0_COLOR}"
			${ECHO} "${CURL_CMD} --no-progress-meter -H \"cid:XXXXX\" ${CLOUD_URL}/api/v1/status/${_jname}"
			continue
		fi
		_ssh_pref=$( substr --pos=0 --len=3 --str="${_ssh}" )
		if [ "${_ssh_pref}" != "ssh" ]; then
			${ECHO} "${N1_COLOR}${CBSD_APP} error: curl error: ${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}" )
		_ssh_post=$( substr --pos=5 --len=${_ssh_len} --str="${_ssh}" )
		#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

		_args=

		# trim for jname= in "$*"
		for i in $*; do
			prefix=
			prefix6=$( substr --pos=0 --len=6 --str="${i}" )
			prefix5=$( substr --pos=0 --len=5 --str="${i}" )
			prefix15=$( substr --pos=0 --len=15 --str="${i}" )
			prefix17=$( substr --pos=0 --len=17 --str="${i}" )
			prefix28=$( substr --pos=0 --len=28 --str="${i}" )
			prefix38=$( substr --pos=0 --len=38 --str="${i}" )

			[ "${prefix15}" = "cbsd_pkg_retry=" ] && continue
			[ "${prefix17}" = "cbsd_pkg_verbose=" ] && continue
			[ "${prefix28}" = "cbsd_pkg_interrupt_on_error=" ] && continue
			[ "${prefix38}" = "cbsd_pkg_bootstrap_interrupt_on_error=" ] && continue
			[ "${prefix6}" = "jname=" ] && continue
			[ "${prefix5}" = "mode=" ] && continue
			if [ -n "${_args}" ]; then
				_args="${_args} ${i}"
			else
				_args="${i}"
			fi
		done

		jexec_cmd="${SSH_CMD} -T -oStrictHostKeyChecking=no -oBatchMode=yes -oConnectTimeout=5 -oServerAliveInterval=10 ${_ssh_sudo_arg} ${_ssh_post}"
		echo "[debug] ${jexec_cmd}"

		case "${mode}" in
			clean)
					${jexec_cmd} pkg-static clean -ya
					;;
			bootstrap|update)
					printf "${N1_COLOR}pkg: [${N2_COLOR}bootstrap...${N1_COLOR}${N0_COLOR}"
					${jexec_cmd} /bin/sh <<EOF
${ENV_CMD} ASSUME_ALWAYS_YES=yes SIGNATURE_TYPE=none IGNORE_OSVERSION=yes pkg bootstrap -f
${ENV_CMD} IGNORE_OSVERSION=yes SIGNATURE_TYPE=none ${TIMEOUT_CMD} 60 pkg update -f
EOF
					_ret=$?
					if [ ${_ret} -eq 0 ]; then
						printf "${N2_COLOR}ok${N1_COLOR}]${N0_COLOR}"
						echo
						break
					else
						printf "${N1_COLOR}bootstrap failed${N1_COLOR}]${N0_COLOR}"
						echo
						echo "${_ret}"
					fi
				;;
			install)
					${ECHO} "${N1_COLOR}pkg install, attempt: ${N2_COLOR}${_i}/${cbsd_pkg_retry}${N0_COLOR}"
					${jexec_cmd} /bin/sh <<EOF
${ENV_CMD} SIGNATURE_TYPE=none ASSUME_ALWAYS_YES=yes IGNORE_OSVERSION=yes pkg install -g -U -y ${_args}
EOF
					_ret=$?
					if [ ${_ret} -eq 0 ]; then
						printf "${N2_COLOR}ok${N1_COLOR}]${N0_COLOR}"
						echo
						break
					else
						printf "${N1_COLOR}bootstrap failed${N1_COLOR}]${N0_COLOR}"
						echo
						echo "${_ret}"
					fi
				;;
			*)
				echo "CBSDfile/API unsupported yet: ${mode}"
				;;
		esac


#	${jexec_cmd} <<CBSD_EOF
#${cmd}
#CBSD_EOF
	done
	exit 0
fi

emulator="jail" # for jname_is_multiple
if [ -n "${all_jail_list}" ]; then
	jail_list="${all_jail_list}"
else
	jail_list=
	jname_is_multiple
fi

if [ -n "${jail_list}" ]; then
	num_jails=0
	for i in ${jail_list}; do
		num_jails=$(( num_jails + 1 ))
	done
	if [ ${num_jails} -eq 1 ]; then
		jname="${jail_list}"
		unset jail_list
	fi
fi

# -n "name of the tools" - show <name> in Info string, e.g: -n jexec, -n "pkg install" ...
# -o uniq_name_of_the_task (one world)
pkg_multi_init()
{
	while getopts "c:n:o:" opt; do
		case "${opt}" in
			c) cmd="${OPTARG}" ;;
			n) _multiple_consumer_name="${OPTARG}" ;;
			o) task_owner="${OPTARG}" ;;
		esac
		shift $(($OPTIND - 1))
	done

	[ -z "${task_owner}" ] && err 1 "${N1_COLOR}multiple_processing_spawn: empty -o multiple_task_owner${N0_COLOR}"

	. ${subrdir}/multiple.subr
	${ECHO} "${N1_COLOR}Hint: Press ${N2_COLOR}'Ctrl+t'${N1_COLOR} to see last logfile line for active task${N0_COLOR}" 1>&2
	task_id=
	task_id_cur=
	# spawn command for all jail
	for jname in ${jail_list}; do
		task_id_cur=$( task mode=new logfile=${tmpdir}/${task_owner}.${jname}.log.$$ client_id=${jname} autoflush=0 owner=${task_owner} ${ENV_CMD} NOCOLOR=1 /usr/local/bin/cbsd pkg jname=${jname} ${cmd} 2>/dev/null )
		sleep 0.1		# dont brute taskdb
		if ! is_number "${task_id_cur}"; then
			task_id="${task_id} ${task_id_cur}"
		fi
	done
	multiple_task_id_all=$( echo ${task_id} | ${TR_CMD} " " "," )
	sleep 0.5
	multiple_processing_spawn -o ${task_owner} -n "${_multiple_consumer_name}"
}

# MAIN for multiple jails
if [ -n "${jail_list}" ]; then
	# multiple jailsastart always non interactive
	if [ -n "${jail_list}" ]; then
		JLIST="${jail_list}"
	fi

	_args=

	# trim for jname= in "$*"
	for i in $*; do
		prefix=
		prefix6=$( substr --pos=0 --len=6 --str="${i}" )
		prefix5=$( substr --pos=0 --len=5 --str="${i}" )
		prefix15=$( substr --pos=0 --len=15 --str="${i}" )
		prefix17=$( substr --pos=0 --len=17 --str="${i}" )
		prefix28=$( substr --pos=0 --len=28 --str="${i}" )
		prefix38=$( substr --pos=0 --len=38 --str="${i}" )

		[ "${prefix15}" = "cbsd_pkg_retry=" ] && continue
		[ "${prefix17}" = "cbsd_pkg_verbose=" ] && continue
		[ "${prefix28}" = "cbsd_pkg_interrupt_on_error=" ] && continue
		[ "${prefix38}" = "cbsd_pkg_bootstrap_interrupt_on_error=" ] && continue
		[ "${prefix6}" = "jname=" ] && continue
		[ "${prefix5}" = "mode=" ] && continue
		if [ -n "${_args}" ]; then
			_args="${_args} ${i}"
		else
			_args="${i}"
		fi
	done

	case "${mode}" in
		clean)
				jexec jname="${jname}" pkg-static clean -ya
				;;
		bootstrap)
				[ "${platform}" = "DragonFly" ] && exit 0
				task_owner="pkg_multiple_bootstrap"
				pkg_multi_init -c "mode=bootstrap" -o ${task_owner} -n "pkg bootstrap"
				;;
		update)
				${TIMEOUT_CMD} 360 jexec jname="${jname}" ${ENV_CMD} SIGNATURE_TYPE=none IGNORE_OSVERSION=yes pkg-static update -f
				;;
		upgrade)
				${TIMEOUT_CMD} 360 jexec jname="${jname}" pkg-static upgrade -g -U -y
				;;
		install|add)
				task_owner="pkg_multiple_install"
				pkg_multi_init -c "mode=install ${_args}" -o ${task_owner} -n "pkg install"
				;;
		remove)
				task_owner="pkg_multiple_remove"
				pkg_multi_init -c "mode=remove ${_args}" -o ${task_owner} -n "pkg remove"
				;;
		info)
				task_owner="pkg_multiple_info"
				pkg_multi_init -c "mode=info ${_args}" -o ${task_owner} -n "pkg info"
				;;
		query)
				task_owner="pkg_multiple_query"
				pkg_multi_init -c "mode=query ${_args}" -o ${task_owner} -n "pkg query"
				;;
		*)
				echo "Multiple unsupported yet for ${mode}"
				;;
	esac

	err 0 "${N1_COLOR}Multiple pkg: ${N2_COLOR}done${N0_COLOR}"
fi

if [ -n "${jname}" ]; then
	. ${subrdir}/rcconf.subr
	[ $? -eq 1 ] && err 1 "${N1_COLOR}No such jail: ${N2_COLOR}${jname}${N0_COLOR}"

	over="${ver}"

	# Determine stable value. Must be after buildconf
	strpos --str="${over}" --search="."

	# auto-detect for stable/release
	pos=$?
	if [ ${pos} -eq 0 ]; then
		stable=1
		ostable=1
	else
		stable=0
		ostable=0
	fi

	[ ${baserw} -eq 1 ] && path=${data}

	if [ ${jid} -eq 0 ]; then
		. ${subrdir}/universe.subr
		readconf buildworld.conf
		init_target_arch
		init_srcdir
		init_basedir
		init_kerneldir
		[ ${nomount} -eq 0 ] && prepare_offline_jail
		makeresolv jname=${jname}
	fi

	if [ "${emulator}" != "jail" -a -n "${emulator}" -a "${emulator}" != "bhyve" ]; then
		. ${subrdir}/emulator.subr
		init_usermode_emul
		CHROOT_EXEC="/usr/sbin/chroot ${path} /bin/${emulator}"
	else
		CHROOT_EXEC="/usr/sbin/chroot ${path}"
	fi

	PKG_PRE="${CHROOT_EXEC}"
	shift
else
	path=
fi

# trim special args
_args=
for i in $*; do
	prefix=
	prefix6=$( substr --pos=0 --len=6 --str="${i}" )
	prefix5=$( substr --pos=0 --len=5 --str="${i}" )
	prefix15=$( substr --pos=0 --len=15 --str="${i}" )
	prefix17=$( substr --pos=0 --len=17 --str="${i}" )
	prefix28=$( substr --pos=0 --len=28 --str="${i}" )
	prefix38=$( substr --pos=0 --len=38 --str="${i}" )
	[ "${prefix15}" = "cbsd_pkg_retry=" ] && continue
	[ "${prefix17}" = "cbsd_pkg_verbose=" ] && continue
	[ "${prefix28}" = "cbsd_pkg_interrupt_on_error=" ] && continue
	[ "${prefix38}" = "cbsd_pkg_bootstrap_interrupt_on_error=" ] && continue
	[ "${prefix6}" = "jname=" ] && continue
	[ "${prefix5}" = "mode=" ] && continue
	if [ -n "${_args}" ]; then
		_args="${_args} ${i}"
	else
		_args="${i}"
	fi
done

if [ -n "${pkglist}" ]; then
	[ ! -s "${pkglist}" ] && err 1 "${N1_COLOR}No such file or file is empty: ${N2_COLOR}${pkglist}${N0_COLOR}"
	pkgtarget=$( ${CAT_CMD} ${pkglist} | ${XARGS_CMD} )
else
	pkgtarget="${_args}"
fi

# init pkg path
init_path

[ -n "${repodir}" ] && PKGARGS="${PKGARGS} -R ${repodir}"
[ -n "${chroot}" ] && PKGARGS="${PKGARGS} -c ${chroot}"

res=0

logfile=$( ${MKTEMP_CMD} )
trap "${RM_CMD} -f ${logfile}" HUP INT ABRT BUS TERM EXIT

case "${mode}" in
	bootstrap)
		[ -z "${jname}" ] && err 1 "${N1_COLOR}bootstrap for jail only${N0_COLOR}"
		pkg_bootstrap > ${logfile} 2>&1
		res=$?
		;;
	clean)
		pkg_clean "${pkgtarget}" > ${logfile} 2>&1
		res=$?
		;;
	install|add)
		pkg_install "${pkgtarget}" > ${logfile} 2>&1
		res=$?
		;;
	remove)
		pkg_remove "${pkgtarget}" > ${logfile} 2>&1
		res=$?
		;;
	info)
		pkg_info "${pkgtarget}"
		res=$?
		;;
	query)
		pkg_query "${pkgtarget}"
		res=$?
		;;
	update)
		pkg_update "${pkgtarget}" > ${logfile} 2>&1
		res=$?
		;;
	upgrade)
		pkg_upgrade "${pkgtarget}" > ${logfile} 2>&1
		res=$?
		;;
	*)
		err 1 "${N1_COLOR}Unknown mode${N0_COLOR}"
esac

if [ ${cbsd_pkg_verbose} -eq 1 ]; then
	if [ -r ${logfile} ]; then
		logfile_bsize=$( get_file_bytes ${logfile} 2>/dev/null )
		[ ${logfile_bsize} -gt 0 ] && ${CAT_CMD} ${logfile}
	fi
else
	if [ ${res} -ne 0 ]; then
		if [ -r ${logfile} ]; then
			logfile_bsize=$( get_file_bytes ${logfile} 2>/dev/null )
			[ ${logfile_bsize} -gt 0 ] && ${CAT_CMD} ${logfile}
		fi
	fi
fi

exit ${res}
