#!/usr/local/bin/cbsd
# fetch http://pkgmir.pkg.freebsd.org/freebsd:11:x86:64/latest/packagesite.txz
#v11.2.0
CBSDMODULE="helpers"
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="cbsd pkg wrapper"
ADDHELP="\
 jname   = work in with jail, can be mask, e.g: '*', 'jail*'\n\
 pkglist = use package list from file\n\
 name    = name of pkg\n\
 repodir = repository configuration directory\n\
 pkgconf = specify pkg.conf to use for pkg\n\
 nomount = 1 do not mount/unmount jail fs\n\
 mode    = add,bootstrap,install,info,query,remove,update,upgrade,clean\n\
 cbsd_pkg_retry - overwrite number of retry when pkg failed (def: 2)\n\
 cbsd_pkg_interrupt_on_error - regulate behavior/errcode upon pkg failed (def: 1)\n\
 cbsd_pkg_bootstrap_interrupt_on_error - regulate behavior upon pkg bootstrap failed (def: 0)\n\
"
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

# 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} ]; then
	[ -z "${CBSDFILE_RECURSIVE}" ] && ${ECHO} "${N1_COLOR}found CBSDfile: ${N2_COLOR}${Makefile}${N0_COLOR}"
	. ${Makefile}
	all_jail_list=$( ${EGREP_CMD} '^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=$( ${MD5_CMD} -qs "${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)
				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 path and bootstrap pkg if necessary
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=$( ${STAT_CMD} -f "%z" ${logfile} 2>/dev/null )
		[ ${logfile_bsize} -gt 0 ] && ${CAT_CMD} ${logfile}
	fi
else
	if [ ${res} -ne 0 ]; then
		if [ -r ${logfile} ]; then
			logfile_bsize=$( ${STAT_CMD} -f "%z" ${logfile} 2>/dev/null )
			[ ${logfile_bsize} -gt 0 ] && ${CAT_CMD} ${logfile}
		fi
	fi
fi

exit ${res}
