#!/usr/local/bin/cbsd
#v13.0.11
CBSDMODULE="sys"
MYARG="jname mode"
MYOPTARG="cloudengine fromfile"
MYDESC="cloud-init helper t generate CI yaml"
ADDHELP="
${H3_COLOR}Description${N0_COLOR}:

 Prepare cloud-init/cloudbase related manifests.

${H3_COLOR}Options${N0_COLOR}:

 ${N2_COLOR}cloudengine=${N0_COLOR} - target engine, 'cloud-init' (default), 'cloudinit-base' or 'fire-*';
 ${N2_COLOR}fromfile=${N0_COLOR}    - load params from file;
 ${N2_COLOR}jname=${N0_COLOR}       - target env;
 ${N2_COLOR}mode=${N0_COLOR}        - mode can be: 'show', 'gen';
"

. ${subrdir}/nc.subr
. ${cbsdinit}
. ${subrdir}/rcconf.subr

case "${emulator}" in
	bhyve|xen|qemu)
		;;
	*)
		log_err 1 "${N1_COLOR}${CBSD_APP} error: unsupported emulator for ${jname}: ${N2_COLOR}${emulator}${N0_COLOR}"
		;;
esac

[ -z "${cloudengine}" ] && cloudengine="cloud-init"

cbsd_cloud_init=0
readconf cloud-init-extras.conf

_MYDIR="${distdir}/modules/bsdconf.d"
SERVICE="cloudinit"

case "${cloudengine}" in
	tiny-cloud)
		# build-in
		cloud_init_dir="${jailsysdir}/${jname}/cloud-init"
		cloud_data_files="meta-data user-data"
		;;
	cloud-init)
		cloud_init_dir="${jailsysdir}/${jname}/cloud-init"
		cloud_data_files="meta-data network-config user-data"
		[ ${cbsd_cloud_init} -eq 1 ] && cloud_data_files="${cloud_data_files} cbsd-network-config"
		;;
	cloudinit-base)
		cloud_init_dir="${jailsysdir}/${jname}/cloud-init/openstack/latest"
		cloud_data_files="meta_data.json network_data.json"
		;;
	fire*)
		cloud_init_dir="${jailsysdir}/${jname}/cloud-init"
		cloud_data_files="network-config user-data zzz_resize"
		;;
	homeass)
		# build-in
		cloud_init_dir="${jailsysdir}/${jname}/cloud-init"
		cloud_data_files="network-config"
		;;
	*)
		err 1 "${N1_COLOR}cloudinit: unknown cloud engine: ${L2_COLOR}${cloudengine}${N0_COLOR}"
		;;
esac

master_prestart_dir="${jailsysdir}/${jname}/master_prestart.d"

f_getvar()
{
	local __var_to_get="$1" __var_to_set="$2"
	[ "$__var_to_set" ] || local value
	eval [ \"\${$__var_to_get+set}\" ]
	local __retval=$?
	eval ${__var_to_set:-value}=\"\${$__var_to_get}\"
	[ "$__var_to_set" ] || { [ "$value" ] && echo "$value"; }
	return $__retval
}


# -s source: "meta-data", "network-config" or "user-data"
show()
{
	local _source=
	local _tpldir="${_MYDIR}/cloud-tpl/${ci_template}"
	local _keytest= _keytest2=
	local _nomask=1

	[ ! -d ${_tpldir} ] && err 1 "${N1_COLOR}cloudinit: no template dir: ${N2_COLOR}${_tpldir}${N0_COLOR}"

	while getopts "s:" opt; do
		case "${opt}" in
			s) _source="${OPTARG}" ;;
		esac
		shift $(($OPTIND - 1))
	done

	ci_user_pw_user_lock="false"
	[ -z "${ci_user_pw_user}" ] && ci_user_pw_user='*'
	ci_user_pw_root_lock="false"
	[ -z "${ci_user_pw_root}" ] && ci_user_pw_root='*'

	if [ -z "${default_ci_interface_mtu}" ]; then
		ci_interface_mtu="1500"
		oci_interface_mtu="1500"
	else
		oci_interface_mtu="${default_ci_interface_mtu}"
	fi

	# todo: test for empty values
	case "${_source}" in
		meta-data)
			if [ "${cloudengine}" = "tiny-cloud" ]; then
				# olevole comment: /usr/local/cbsd/modules/bsdconf.d/cloud-tpl/tiny-cloud/README.md
				unset ci_pubkey ci_pubkey2

				# global pubkey
				if [ -z "${ci_pubkey}" ]; then
					ci_pubkey="${ci_user_pubkey}"
				fi

				if [ -n "${ci_user_pubkey2}" ]; then
					ci_pubkey2="${ci_user_pubkey2}"
				fi

				if [ -n "${ci_pubkey}" ]; then
					# ci_pubkey test
					. ${subrdir}/settings-tui-virtual.subr	# for is_valid_ssh_key
					if [ -r "${workdir}/${ci_pubkey}" ]; then
						_keytest=$( ${GREP_CMD} -v '#' ${workdir}/${ci_pubkey} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest}"; then
							echo "cloudinit: invalid ssh key from ${workdir}/${ci_pubkey} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest}]"
							return 1
						fi
					elif [ -r "${ci_pubkey}" ]; then
						_keytest=$( ${GREP_CMD} -v '#' ${ci_pubkey} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest}"; then
							echo "cloudinit: invalid ssh key from ${ci_pubkey} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest}]"
							return 1
						fi
					else
						if ! is_valid_ssh_key "${ci_pubkey}"; then
							echo "cloudinit: invalid ssh key: [${ci_pubkey}]. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							return 1
						fi
						_keytest="${ci_pubkey}"
					fi
				fi

				if [ -n "${ci_pubkey2}" ]; then
					# ci_pubkey2 test
					. ${subrdir}/settings-tui-virtual.subr	# for is_valid_ssh_key
					if [ -r "${workdir}/${ci_pubkey2}" ]; then
						_keytest2=$( ${GREP_CMD} -v '#' ${workdir}/${ci_pubkey2} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest2}"; then
							echo "cloudinit: invalid ssh key from ${workdir}/${ci_pubkey2} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest2}]"
							return 1
						fi
					elif [ -r "${ci_pubkey2}" ]; then
						_keytest2=$( ${GREP_CMD} -v '#' ${ci_pubkey2} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest2}"; then
							echo "cloudinit: invalid ssh key from ${ci_pubkey2} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest2}]"
							return 1
						fi
					else
						if ! is_valid_ssh_key "${ci_pubkey2}"; then
							echo "cloudinit: invalid ssh key: [${ci_pubkey2}]. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							return 1
						fi
						_keytest2="${ci_pubkey2}"
					fi
				fi
			fi

			[ -r ${_tpldir}/${_source} ] && ${SED_CMD} -e "s:%%ci_jname%%:${ci_jname}:g" \
			-e "s:%%ci_fqdn%%:${ci_fqdn}:g" \
			-e "s:%%ci_pubkey%%:${_keytest}:g" \
			-e "s:%%ci_pubkey2%%:${_keytest2}:g" \
			${_tpldir}/${_source}
			;;
		meta_data.json)
			${SED_CMD} -e "s:%%ci_jname%%:${ci_jname}:g" \
			-e "s:%%ci_fqdn%%:${ci_fqdn}:g" \
			-e "s:%%ci_user_pw_root%%:${ci_user_pw_root}:g" \
			${_tpldir}/${_source}
			;;
		network-config)

			[ -r ${_tpldir}/10-${_source} ] && ${CAT_CMD} ${_tpldir}/10-${_source}

			interface_num=1
			gw_set=0

			# todo: ci_interfaceX - unlim?
			for interface in ${ci_interface} ${ci_interface2}; do

				nic_id=$(( interface_num - 1 ))
				interface_name="${ci_interface_name}${nic_id}"

				# CLOUD HACK: only first nic used global ci_interface_mtu
				if [ ${interface_num} -ne 1 ]; then
					ci_interface_mtu="1500"
				else
					ci_interface_mtu="${oci_interface_mtu}"
				fi

				[ -r ${_tpldir}/20-${_source}-interface ] && ${SED_CMD} -e "s:%%ci_interface_name%%:${interface_name}:g" \
				-e "s:%%ci_interface_mtu%%:${ci_interface_mtu}:g" \
				${_tpldir}/20-${_source}-interface

				if [ ${interface_num} -eq 1 ]; then

					OIFS="${IFS}"
					IFS=","

					for _pureip in ${ci_ip4_addr}; do

						IFS="${OIFS}"

						if [ "${_pureip}" = "REALDHCP" ]; then
							${SED_CMD} -e "s#%%uuid%%#${uuid}#g" ${_tpldir}/30-${_source}-dhcp4
							continue
						fi

						ipwmask ${_pureip}
						[ -z "${IWM}" -o "${_i}" = "0" ] && continue

						strpos --str="${_pureip}" --search="/"
						_nomask=$?

						iptype ${IWM}
						_inet=$?

						#cloudinit-base not support /prefix as ci_ip4_addr
						if [ "${cloudengine}" != "cloudinit-base" ]; then
							case ${_inet} in
								1)
									ipv4_true="true"
									ipv6_true="false"
									if [ ${_nomask} -eq 0 ]; then
										_pureip="${_pureip}/24"
									else
										_mask="${_pureip#*/}"
										if is_number "${_mask}"; then
											err 1 "${N1_COLOR}${CBSD_APP}: v4 prefix not a number: ${_mask}: ${N2_COLOR}${_pureip}${N1_COLOR}"
										else
											_pureip="${IWM}/${_mask}"
										fi
									fi
									;;
								2)
									ipv4_true="false"
									ipv6_true="true"
									if [ ${_nomask} -eq 0 ]; then
										_pureip="${_pureip}/64"
									else
										_mask="${_pureip#*/}"
										if is_number "${_mask}"; then
											err 1 "${N1_COLOR}${CBSD_APP}: v6 prefix not a number: [${_mask}]: ${N2_COLOR}${_pureip}${N1_COLOR}"
										else
											_pureip="${IWM}/${_mask}"
										fi
									fi
									;;
								*)
									log_err 1 "${N1_COLOR}Not in bhyve emulator: ${N2_COLOR}${jname}${N0_COLOR}"
									;;
							esac
						fi

						# default router only one ( fix BSD )
						# use oci_gw4 instead of ci_gw4 - dont prune original value
						if [ ${interface_num} -eq 1 ]; then
							if [ ${gw_set} -eq 0 ]; then
								oci_gw4="${ci_gw4}"
							else
								oci_gw4=
							fi
						fi

						# for firestarter only:
						# "s:%%ci_interface_name%%:${interface_name}:g" \
						# -e "s:%%ci_fqdn%%:${ci_fqdn}:g" \
						# -e "s:%%ci_nameserver_address%%:${ci_nameserver_address}:g" \
						# -e "s:%%ci_nameserver_search%%:${ci_nameserver_search}:g" \
						# -e "s:%%ci_interface_mtu%%:${ci_interface_mtu}:g" \


						${SED_CMD} -e "s#%%ipv4_true%%#${ipv4_true}#g" \
						-e "s#%%ipv6_true%%#${ipv6_true}#g" \
						-e "s#%%ci_ip4_addr%%#${_pureip}#g" \
						-e "s#%%uuid%%#${uuid}#g" \
						-e "s#%%ci_gw4%%#${oci_gw4}#g" \
						-e "s:%%ci_interface_name%%:${interface_name}:g" \
						-e "s:%%ci_fqdn%%:${ci_fqdn}:g" \
						-e "s:%%ci_nameserver_address%%:${ci_nameserver_address}:g" \
						-e "s:%%ci_nameserver_search%%:${ci_nameserver_search}:g" \
						-e "s:%%ci_interface_mtu%%:${ci_interface_mtu}:g" \
						${_tpldir}/30-${_source}-static

						gw_set=1

						IFS=","
					done
					IFS="${OIFS}"
				fi

				## 
				if [ ${interface_num} -eq 2 -a -n "${ci_ip4_addr2}" ]; then

					ipwmask ${ci_ip4_addr2}
					[ -z "${IWM}" -o "${_i}" = "0" ] && continue

					strpos --str="${ci_ip4_addr2}" --search="/"
					_nomask=$?

					OIFS="${IFS}"
					IFS=","

					for _pureip in ${ci_ip4_addr2}; do

						IFS="${OIFS}"

						iptype ${_pureip}
						_inet=$?

						case ${_inet} in
							1)
								ipv4_true="true"
								ipv6_true="false"
								if [ ${_nomask} -eq 0 ]; then
									_pureip="${_pureip}/24"
								else
									_mask="${_pureip#*/}"
									if is_number "${_mask}"; then
										err 1 "${N1_COLOR}${CBSD_APP}: second-nic: v4 prefix not a number: ${_mask}: ${N2_COLOR}${_pureip}${N1_COLOR}"
									else
										_pureip="${IWM}/${_mask}"
									fi
								fi
								;;
							2)
								ipv4_true="false"
								ipv6_true="true"
								if [ ${_nomask} -eq 0 ]; then
									_pureip="${_pureip}/64"
								else
									_mask="${_pureip#*/}"
									if is_number "${_mask}"; then
										err 1 "${N1_COLOR}${CBSD_APP}: second-nic: v6 prefix not a number: ${_mask}: ${N2_COLOR}${_pureip}${N1_COLOR}"
									else
										_pureip="${IWM}/${_mask}"
									fi
								fi
								;;
							*)
								log_err 1 "${N1_COLOR}Not in bhyve emulator: ${N2_COLOR}${jname}${N0_COLOR}"
								;;
						esac

						oci_gw4="${ci_gw42}"
						#oci_gw4="${ci_gw42}%${interface_name}"

						${SED_CMD} -e "s#%%ipv4_true%%#${ipv4_true}#g" \
						-e "s#%%ipv6_true%%#${ipv6_true}#g" \
						-e "s#%%ci_ip4_addr%%#${_pureip}#g" \
						-e "s#%%uuid%%#${uuid}#g" \
						-e "s#%%ci_gw4%%#${oci_gw4}#g" \
						${_tpldir}/30-${_source}-static
						IFS=","
					done
					IFS="${OIFS}"
				fi

				interface_num=$(( interface_num + 1 ))
				gw_set=0
			done

			[ -r ${_tpldir}/40-${_source} ] && ${SED_CMD} -e "s:%%ci_nameserver_address%%:${ci_nameserver_address}:g" \
			-e "s:%%ci_nameserver_search%%:${ci_nameserver_search}:g" \
			${_tpldir}/40-${_source}
			;;
		cbsd-network-config)
			OIFS="${IFS}"
			IFS=","

			iface=0

			# wait for regen cloud-apply with 'dhcp4' and 'dhcp6' support
			dhcp_work_around=0

			for _pureip in ${ci_ip4_addr} ${ci_ip4_addr2}; do
				IFS="${OIFS}"

				if [ "${_pureip}" != "REALDHCP" ]; then

					ipwmask ${_pureip}
					[ -z "${IWM}" -o "${_i}" = "0" ] && continue

					strpos --str="${_pureip}" --search="/"
					_nomask=$?

					iptype ${IWM}
					_inet=$?

					case ${_inet} in
						1)
							ipv4_true="true"
							ipv6_true="false"
							if [ ${_nomask} -eq 0 ]; then
								_pureip="${_pureip}/24"
							else
								_mask="${_pureip#*/}"
								if is_number "${_mask}"; then
									err 1 "${N1_COLOR}${CBSD_APP}: prefix not a number: ${_mask}: ${N2_COLOR}${_pureip}${N1_COLOR}"
								else
									_pureip="${IWM}/${_mask}"
								fi
							fi
							;;
						2)
							ipv4_true="false"
							ipv6_true="true"
							if [ ${_nomask} -eq 0 ]; then
								_pureip="${_pureip}/64"
							else
								_mask="${_pureip#*/}"
								if is_number "${_mask}"; then
									err 1 "${N1_COLOR}${CBSD_APP}: prefix not a number: ${_mask}: ${N2_COLOR}${_pureip}${N1_COLOR}"
								else
									_pureip="${IWM}/${_mask}"
								fi
							fi
							;;
						*)
							log_err 1 "${N1_COLOR}Unknown IP address for ${jname}: ${N2_COLOR}${IWM}${N0_COLOR}"
							;;
					esac

					if [ ${iface} -eq 1 ]; then
						oci_gw4="${ci_gw42}"
					fi

					echo "ip${iface}=\"${_pureip}\""
					echo "gw${iface}=\"${oci_gw4}\""
					#echo "gw${iface}=\"${ci_gw42}%vtnet${iface}\""
					iface=$(( iface + 1 ))
				else
					dhcp_work_around=1		# remove me after regen gold images with new cloud-init 'dhcp[4,6]' support
					echo "ip${iface}=\"dhcp4\""
					# REALDHCP
					# todo: REALDHCPv6 ?
				fi
				IFS=","
			done
			IFS="${OIFS}"

			# CLOUD HACK: only first nic used global ci_interface_mtu
			echo "mtu=\"${oci_interface_mtu}\""
			;;
		network_data.json)
			# cloudbase-init doesnt support /prefix in ci_ip4_addr
			if [ "${cloudengine}" = "cloudinit-base" ]; then
				ipwmask ${ci_ip4_addr}
				[ -z "${IWM}" -o "${_i}" = "0" ] && continue
				ci_ip4_addr="${IWM}"
			fi

			${SED_CMD} -e "s:%%ci_interface%%:${ci_interface}:g" \
			-e "s:%%ci_ip4_addr%%:${ci_ip4_addr}:g" \
			-e "s:%%uuid%%:${uuid}:g" \
			-e "s:%%ci_interface_mtu%%:${ci_interface_mtu}:g" \
			-e "s:%%ci_gw4%%:${ci_gw4}:g" \
			-e "s:%%ci_nameserver_address%%:${ci_nameserver_address}:g" \
			-e "s:%%ci_nameserver_search%%:${ci_nameserver_search}:g" \
			-e "s#%%ci_nic_hwaddr0%%#${ci_nic_hwaddr0}#g" \
			${_tpldir}/${_source}
			;;
		user-data)
			# user list here
			# This is a multi-part compound file.
			# Without jinja/erb this is really entangled code ;-(

			# change root password needed?
			need_root_chpasswd=0

			if [ "${cloudengine}" = "tiny-cloud" ]; then
				# olevole comment: /usr/local/cbsd/modules/bsdconf.d/cloud-tpl/tiny-cloud/README.md
				${SED_CMD} -e "s:%%ci_interface%%:${ci_interface}:g" \
				-e "s:%%ci_ip4_addr%%:${ci_ip4_addr}:g" \
				-e "s:%%ci_gw4%%:${ci_gw4}:g" \
				${_tpldir}/user-data-static
				return 0
			fi

			[ -r ${_tpldir}/10-user-data ] && ${CAT_CMD} ${_tpldir}/10-user-data		# header

			for i in ${ci_user_add}; do
				# change user password needed?
				need_user_chpasswd=0

				unset login epw ci_pw ci_fullname ci_secgroup ci_group ci_shell res err
				unset ci_pubkey ci_pubkey2
				ci_login="${i}"

				f_getvar ci_user_pw_${i} ci_pw
				if [ -z "${ci_pw}" ]; then
					f_getvar ci_user_pw ci_pw
				fi

				f_getvar ci_user_pw_${i}_crypt ci_epw
				if [ -z "${ci_epw}" ]; then
					f_getvar ci_user_pw_crypt ci_epw
				fi

				f_getvar ci_user_gecos_${i} ci_fullname
				if [ -z "${ci_fullname}" ]; then
					f_getvar ci_user_gecos ci_fullname
				fi

				f_getvar ci_user_home_${i} ci_home
				if [ -z "${ci_home}" ]; then
					f_getvar ci_user_home ci_home
				fi

				f_getvar ci_user_shell_${i} ci_shell
				if [ -z "${ci_shell}" ]; then
					f_getvar ci_user_shell ci_shell
				fi

				f_getvar ci_user_member_groups_${i} ci_secgroup
				if [ -z "${ci_secgroup}" ]; then
					f_getvar ci_user_member_groups ci_secgroup
				fi

				f_getvar ci_user_pubkey_${i} ci_pubkey
				if [ -z "${ci_pubkey}" ]; then
					f_getvar ci_user_pubkey ci_pubkey
				fi

				[ -z "${ci_shell}" ] && ci_shell="/bin/sh"

				# global pubkey
				if [ -z "${ci_pubkey}" ]; then
					ci_pubkey="${ci_user_pubkey}"
				fi

				if [ -n "${ci_user_pubkey2}" ]; then
					ci_pubkey2="${ci_user_pubkey2}"
				fi

				if [ -n "${ci_pubkey}" ]; then
					# ci_pubkey test
					. ${subrdir}/settings-tui-virtual.subr	# for is_valid_ssh_key
					if [ -r "${workdir}/${ci_pubkey}" ]; then
						_keytest=$( ${GREP_CMD} -v '#' ${workdir}/${ci_pubkey} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest}"; then
							echo "cloudinit: invalid ssh key from ${workdir}/${ci_pubkey} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest}]"
							return 1
						fi
					elif [ -r "${ci_pubkey}" ]; then
						_keytest=$( ${GREP_CMD} -v '#' ${ci_pubkey} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest}"; then
							echo "cloudinit: invalid ssh key from ${ci_pubkey} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest}]"
							return 1
						fi
					else
						if ! is_valid_ssh_key "${ci_pubkey}"; then
							echo "cloudinit: invalid ssh key: [${ci_pubkey}]. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							return 1
						fi
						_keytest="${ci_pubkey}"
					fi
				fi

				if [ -n "${ci_pubkey2}" ]; then
					# ci_pubkey2 test
					. ${subrdir}/settings-tui-virtual.subr	# for is_valid_ssh_key
					if [ -r "${workdir}/${ci_pubkey2}" ]; then
						_keytest2=$( ${GREP_CMD} -v '#' ${workdir}/${ci_pubkey2} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest2}"; then
							echo "cloudinit: invalid ssh key from ${workdir}/${ci_pubkey2} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest2}]"
							return 1
						fi
					elif [ -r "${ci_pubkey2}" ]; then
						_keytest2=$( ${GREP_CMD} -v '#' ${ci_pubkey2} | ${GREP_CMD} . | ${HEAD_CMD} -n1 )
						if ! is_valid_ssh_key "${_keytest2}"; then
							echo "cloudinit: invalid ssh key from ${ci_pubkey2} file. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							echo "found: [${_keytest2}]"
							return 1
						fi
					else
						if ! is_valid_ssh_key "${ci_pubkey2}"; then
							echo "cloudinit: invalid ssh key: [${ci_pubkey2}]. valid key: ssh-rsa,ssh-ed25519,ecdsa-*,ssh-dsa"
							return 1
						fi
						_keytest2="${ci_pubkey2}"
					fi
				fi

				prefix=$( substr --pos=0 --len=1 --str=${ci_user_pw_user} )
				if [ "${prefix}" = "*" ]; then
					ci_user_pw_user_lock="true"
				else
					ci_user_pw_user_lock="false"
					need_user_chpasswd=1
				fi

				if [ "${ci_user_pw_user_lock}" = "false" ]; then
					[ -r ${_tpldir}/20-user-data-user-password ] && ${SED_CMD} -e "s:%%ci_login%%:${ci_login}:g" \
					-e "s:%%ci_shell%%:${ci_shell}:g" \
					-e "s:%%ci_pubkey%%:${_keytest}:g" \
					-e "s:%%ci_pubkey2%%:${_keytest2}:g" \
					-e "s:%%ci_user_pw_user%%:${ci_user_pw_user}:g" \
					${_tpldir}/20-user-data-user-password
				else
					[ -r ${_tpldir}/20-user-data-user-lockpassword ] && ${SED_CMD} -e "s:%%ci_login%%:${ci_login}:g" \
					-e "s:%%ci_shell%%:${ci_shell}:g" \
					-e "s:%%ci_pubkey%%:${_keytest}:g" \
					-e "s:%%ci_pubkey2%%:${_keytest2}:g" \
					${_tpldir}/20-user-data-user-lockpassword
				fi
			done

			# root user ( combine with users ^^ ? )
			prefix=$( substr --pos=0 --len=1 --str=${ci_user_pw_root} )
			if [ "${prefix}" = "*" ]; then
				ci_user_pw_root_lock="true"
			else
				ci_user_pw_root_lock="false"
				need_root_chpasswd=1
			fi
			if [ "${ci_user_pw_root_lock}" = "false" ]; then
				[ -r ${_tpldir}/20-user-data-root-password ] && ${SED_CMD} -e "s:%%ci_user_pw_root%%:${ci_user_pw_root}:g" \
				${_tpldir}/20-user-data-root-password
			else
				[ -r ${_tpldir}/20-user-data-root-lockpassword ] && ${CAT_CMD} ${_tpldir}/20-user-data-root-lockpassword
			fi

			# chpasswd
			if [ "${need_user_chpasswd}" = "1" -o "${need_root_chpasswd}" = "1" ]; then
				[ -r ${_tpldir}/30-user-data-chpasswd ] && ${CAT_CMD} ${_tpldir}/30-user-data-chpasswd
				[ -r ${_tpldir}/40-user-data-chpasswd-user -a "${need_user_chpasswd}" = "1" ] && ${SED_CMD} -e "s:%%ci_login%%:${ci_login}:g" -e "s:%%ci_user_pw_user%%:${ci_user_pw_user}:g" ${_tpldir}/40-user-data-chpasswd-user
				[ -r ${_tpldir}/40-user-data-chpasswd-root -a "${need_root_chpasswd}" = "1" ] && ${SED_CMD} -e "s:%%ci_user_pw_root%%:${ci_user_pw_root}:g" ${_tpldir}/40-user-data-chpasswd-root
				[ -r ${_tpldir}/50-user-data-chpasswd ] && ${CAT_CMD} ${_tpldir}/50-user-data-chpasswd
			fi
			;;
		zzz_resize)
			# must be last
			[ ${_tpldir}/50-resize ] && ${CAT_CMD} ${_tpldir}/50-resize
			;;
	esac

	return 0
}

# MAIN
if [ -n "${fromfile}" ]; then

	[ ! -r "${fromfile}" ] && err 1 "${N1_COLOR}cloudinit: no such fromfile: ${N2_COLOR}${fromfile}${N0_COLOR}"

	unset ci_template ci_user_pw_root ci_user_add ci_ip4_addr ci_gw4 ci_nameserver_address ci_nameserver_search
	unset ci_jname ci_fqdn ci_interface ci_ip4_addr ci_gw4 ci_nameserver_address ci_user_pw_user ci_user_pw_root
	unset ci_nic_hwaddr0 ci_user_pubkey ci_user_pubkey2 ci_interface_mtu default_ci_interface_mtu

	. ${fromfile}

	[ -z "${ci_template}" ] && err 1 "${N1_COLOR}No ci_template${N0_COLOR}"

	case "${vm_os_type}" in
		windows)
			[ -z "${ci_user_add}" ] && ci_user_add="windows"
			;;
	esac

	case "${mode}" in
		show)
			echo $cloud_data_files
			for i in ${cloud_data_files}; do
				show -s ${i}
				ret=$?
				[ ${ret} -ne 0 ] && err ${ret} "cloudinit error"
			done
			;;
		gen)
			[ -n "${cloud_init_dir}" -a ! -d "${cloud_init_dir}" ] && ${MKDIR_CMD} -p ${cloud_init_dir}
			for i in ${cloud_data_files}; do
				show -s ${i} > ${cloud_init_dir}/${i}
				ret=$?
				if [ ${ret} -ne 0 ]; then
					${CAT_CMD} ${cloud_init_dir}/${i}
					err ${ret} "cloudinit error"
				fi
			done

			if [ "${ci_adjust_inteface_helper}" = "1" ]; then
				[ ! -d "${master_prestart_dir}" ] && ${MKDIR_CMD} -p ${master_prestart_dir}
				${CP_CMD} -a ${_MYDIR}/cloud-master_prestart.d/cloud_init_set_netname.sh ${master_prestart_dir}/
			fi

			# remove me after regen gold/cloud-init image with 'dhcp[4,6] support
			[ "${dhcp_work_around}" = "1" ] && ${RM_CMD} -f ${cloud_init_dir}/cbsd-network-config

			case "${cloudengine}" in
				fire*)
					if [ ! -r ${data}/dsk2.vhd ]; then
						bhyve-dsk mode=attach jname=${jname} dsk_controller=virtio-blk dsk_size=1m imgtype=md
					fi
					;;
			esac
			;;
		*)
			err 1 "${N1_COLOR}cloudinit helper: unknown mode: ${N2_COLOR}${mode}${N0_COLOR}"
	esac

	exit ${ret}
fi

case "${mode}" in
	show)
		echo "SHOW"
		err=$?
		;;
esac

err ${err} "${res}"
