# LSB output backend
# shellcheck shell=bash

gen_shebang() {
	# Shebang
	printf '%s\n' '#! /bin/sh'
}

gen_init_d_script() {
	printf '%s\n' '# kFreeBSD does not accept scripts as interpreters, using #!/bin/sh and sourcing.'
	printf '%s\n' 'if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then'
	printf '%s\n' '   set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script'
	printf '%s\n' 'fi'
}

gen_start() {
	case "${install[WantedBy]:-}" in
	    	'') ;;
		sysinit.target) printf 'S' ;;
		single.target|basic.target) printf '1' ;;
		*.target) printf '2 3 4 5' ;;
		*) echo "WARNING: unknown start target runlevel ${install[WantedBy]}" >&2 ;;
	esac
}

gen_stop() {
	case "${install[WantedBy]:-}" in
	    	'') ;;
		*.target) printf '0 1 6' ;;
		*) echo "WARNING: unknown stop target runlevel ${install[WantedBy]}" >&2 ;;
	esac
}

munge_deps() {
    self=$(local -; set +o noglob; find /etc/insserv.conf* -type f -exec  awk "/$(basename "$lsb_init_script")/ {print \$1}" '{}' ';')
    for key in Requires Wants After Before; do
	for dep in ${depends[$key]:-}; do
	    if [[ "$dep" == "$self" ]] ; then
		remove_depend "$dep" "$key"
	    fi
	done
    done
    add_depends Wants ${depends[After]:-}
    for dep in ${depends[Requires]:-}; do
	remove_depend "$dep" Wants
    done
    for dep in ${depends[Wants]:-}; do
	remove_depend "$dep" Before
    done
}

gen_lsb_header() {
    # Uncomment to show the data parsed by the frontend
    # declare -p unit  >&2
    # declare -p service >&2
    # declare -p depends >&2
    # declare -p install >&2

	munge_deps
	printf '%s\n' '### BEGIN INIT INFO'
	printf '%s %s\n' '# Provides:' "$(basename "${systemd_unit%.*}")${install[Alias]:+ ${install[Alias]%.*}}"
	printf '%s %s\n' '# Required-Start:' "${depends[Requires]:-}"
	printf '%s %s\n' '# Required-Stop:' "${depends[Requires]:-}"
	[[ "${depends[Wants]:-}" ]] && printf '%s %s\n' '# Should-Start:' "${depends[Wants]}"
	[[ "${depends[Wants]:-}" ]] && printf '%s %s\n' '# Should-Stop:' "${depends[Wants]}"
	printf '%s %s\n' '# Default-Start:' "$(gen_start)"
	printf '%s %s\n' '# Default-Stop:'  "$(gen_stop)"
	[[ "${depends[Before]:-}" ]] && printf '%s %s\n' '# X-Start-Before:' "${depends[Before]}"
	[[ "${depends[Before]:-}" ]] && printf '%s %s\n' '# X-Stop-After:'  "${depends[Before]}"
#	printf '%s %s\n' '# X-Interactive:' 'true'
	printf '%s %s\n' '# Description:' "${unit[Description]:-None provided}"
	printf '%s\n' '### END INIT INFO'
}

gen_lsb_environment() {
	gen_environment

	if [[ "${service[ExecStop]:-}${service[ExecReload]:-}" =~ '$MAINPID' ]]; then
		print_directive MAINPID '$([ ! -f "/run/${DAEMON}.pid" ] || cat "/run/${DAEMON}.pid")'
	fi
}

gen_directives() {
    if [[ "${unit[Description]:-}" ]]; then
	print_directive DESC "${unit[Description]}"
    fi
    print_directive umask "${service[UMask]:-}"
    if [[ -n "${service[ExecStart]:-}" ]]; then
	if [[ "${service[Type]}" == 'oneshot' ]] ; then
	    print_directive DAEMON none
	    print_directive NAME "$(basename "${systemd_unit%.*}")"
	    print_directive TYPE oneshot
	    gen_lsb_environment
	    for key in "${!lsb[@]}"; do
		# Skip directives just for s-s-d
		[[ "$key" != START_ARGS ]] || continue
		print_directive "$key" "${lsb[$key]}"
	    done
	    print_sh_function do_start_cmd_override "$(handle_exec_prefixes "${service[ExecStart]}" runas)"
	else
	    set -- ${service[ExecStart]}
	    if [[ "${service[Type]}" == 'socket' ]]; then
		print_directive NAME "$(basename "${systemd_unit%.*}")"
		print_directive COMMAND_NAME none
		print_directive DAEMON none
		lsb[START_ARGS]+=' --startas /usr/bin/socket-activate'
	    else
		case $1 in
		    /*) print_directive DAEMON "$1" ;;
		    -*) print_directive DAEMON "${1#-}"
			echo "TODO: ignore s-s-d exit status" >&2
			;;
		    @*|+*|!*) echo "WARNING: ignoring special exec prefix" >&2
			      print_directive DAEMON "${1##@|+|!|!!}"
			      ;;
		    *) print_directive DAEMON "$(PATH=/usr/sbin:/usr/bin:/sbin:/bin: which "$1")" ;;
		esac
	    fi
	    shift
	    gen_lsb_environment
	    print_directive DAEMON_ARGS "$*"
	    for key in "${!lsb[@]}"; do
		print_directive "$key" "${lsb[$key]}"
	    done
	fi
    fi
    while read -r ulimit ; do
    	    printf 'ulimit %s\n' "$ulimit"
    done < <(gen_ulimit_args)
}

# Based on backends/openrc but with /lib/lsb/init-functions functions
gen_pre_checks() {
	for constraint in Assert Condition; do
		for test in ACPower Architecture Capability ControlGroupController CPUFeature CPUs DirectoryNotEmpty Environment FileIsExecutable FileNotEmpty Firmware FirstBoot Group Host KernelCommandLine KernelVersion Memory NeedsUpdate OSRelease PathExists PathExistsGlob PathIsDirectory PathIsEncrypted PathIsMountPoint PathIsReadWrite PathIsSymbolicLink Security User Virtualization; do
		    while
			read -r trigger
			read -r pre
			read -r p; do
			t=$(gen_test_case "$test" "$pre" "$p")
			if [ "${t}" ]; then
					if [ "${trigger}" ] ; then
					    triggers+=$'\n'"( $t ) ||"
					else
					    [ $constraint = 'Assert' ] && fail="echo \"Prohibited by ${constraint}${test} ${pre}${p}\"; exit 1" ||
						    fail="do_start_cmd_override() { log_warning_msg \" .. Skipped due to ${constraint}${test} ${pre}${p}\" ; }"
					    echo "$t || $fail"
					fi
				else
					echo "WARNING: unsupported test: $constraint $test ${pre}${p}" >&2
					echo ": # WARNING: skipped unsupported ${constraint}${test} ${pre}${p}"
				fi
			done < <(split_constraint "${unit[${constraint}${test}]:-}")
		done
	done
	if [[ "${triggers:-}" ]] ; then
	    printf '( # Triggering conditions\n'
	    # Remove the final trailing '||'
	    print_lines "${triggers%||}" ' '
	    printf ') || do_start_cmd_override() { log_warning_msg " .. Skipped due to no Triggering Conditions" ; }'
	fi
}

gen_functions() {
	# Combine pre-checks and handling of special exec prefixes
	service[ExecStartPre]=$(cat<<EOF
$(gen_pre_checks)
$(handle_exec_prefixes "${service[ExecStartPre]:-}" runas)
EOF
	       )
	# This appears at odds with init-d-script(5) which states:-
	#
	#  Additionally, it is possible to change the behaviour of the resulting
	#  shell script by overriding some of the internal functions.  To do so,
	#  define function with an _override suffix.
	#
	# However, this naming doesn't seem to apply to functions that are no-op.
	print_sh_function do_start_prepare "${service[ExecStartPre]:-}"
	print_sh_function do_start_cleanup  "$(handle_exec_prefixes "${service[ExecStartPost]:-}" runas)"
	print_sh_function do_stop_prepare  "$(handle_exec_prefixes "${service[ExecStopPre]:-}" runas)"
	print_sh_function do_stop_cleanup  "$(handle_exec_prefixes "${service[ExecStopPost]:-}" runas)"
	print_sh_function do_stop_cmd_override  "$(handle_exec_prefixes "${service[ExecStop]:-}" runas)"
	print_sh_function do_reload "$(handle_exec_prefixes "${service[ExecReload]:-}" runas)"
}

export_service() {
	systemd_unit=$1
	base_dir=$2
	init_dir="${base_dir}/init.d"
	mkdir -p "${init_dir}"

	if [[ $(basename "${systemd_unit%/*}") == 'user' ]] ;  then
		echo "LSB backend is not compatible with user services, skipping" >&2
		exit 0
	fi

	lsb_init_script="${init_dir}/$(basename "${systemd_unit%.*}")"

	if [[ -z "${lsb_init_script##*@}" ]] ; then
		echo "LSB backend doesn't support instantiated services, skipping" >&2
		exit 0
	fi

	# array to hold directives
	declare -A lsb

	case "${service[Type]}" in
		oneshot) ;;
		simple|exec|idle|dbus|socket) lsb[PIDFILE]="/run/$(basename "${lsb_init_script}").pid"
			lsb[START_ARGS]="--background --make-pidfile";;
		notify|notify-reload) lsb[PIDFILE]="/run/$(basename "${lsb_init_script}").pid"
			 lsb[START_ARGS]="--background --make-pidfile --notify-await"
			 if [[ "${service[Type]}" == 'notify-reload' ]]; then
			     lsb[RELOAD_SIGNAL]=HUP
			 fi
			;;
		forking) lsb[PIDFILE]=none ;;
	esac

	# run as $user and $group if the systemd service provides one
	if [[ "${service[User]:-}" ]]; then
		local user="${service[User]}"
		if [[ "${service[Group]:-}" ]]; then
			local group="${service[Group]}"
		fi
		lsb[START_ARGS]+=" --chuid ${user}${group:+:"${group}"}"
	fi

	if [[ -n "${service[Nice]:-}" ]]; then
		lsb[START_ARGS]+=" --nicelevel ${service[Nice]}"
	fi
	if [[ -n "${service[IOSchedulingClass]:-}" || -n "${service[IOSchedulingPriority]:-}" ]]; then
		lsb[START_ARGS]+=" --iosched ${service[IOSchedulingClass]:-best-effort}:${service[IOSchedulingPriority]:-4}"
	fi
	if [[ -n "${service[UMask]:-}" ]]; then
		lsb[START_ARGS]+=" --umask ${service[UMask]:-}"
	fi
	if [[ -n "${service[WorkingDirectory]:-}" ]]; then
		lsb[START_ARGS]+=" --chdir ${service[WorkingDirectory]#-}"
	fi
	if [[ -n "${service[RootDirectory]:-}" ]]; then
		lsb[START_ARGS]+=" --chroot ${service[RootDirectory]}"
	fi
	if [[ "$(uname -s)" == 'Linux' ]]; then
		if [[ -n "${service[NoNewPrivileges]:-}" ]] && is_true "${service[NoNewPrivileges]}"; then
			lsb[SETPRIV_ARGS]+="--no-new-privs "
		fi

		if [[ -n "${service[SecureBits]:-}" ]]; then
			secbits=${service[SecureBits]/-/_}
			lsb[SETPRIV_ARGS]+="--securebits +${secbits//[$' \r\t\n']/,+} "
		fi

		if [[ "${service[CapabilityBoundingSet]+defined}" ]] ; then
			case "${service[CapabilityBoundingSet]}" in
			    # start-stop-daemon requires CAP_SETUID and CAP_SETGID for --chuid
			    '') lsb[SETPRIV_ARGS]+="--bounding-set -all${service[User]:+,+CAP_SETUID,+CAP_SETGID} " ;;
			    ~*) caps="${service[CapabilityBoundingSet]//CAP_/,-}"
				if [[ "${service[User]:-}" ]] ; then
			    caps="${caps//,-SET[UG]ID}"
				fi
				lsb[SETPRIV_ARGS]+="--bounding-set +all${caps//[$'~ \r\t\n']} "
				;;
			    *) caps="${service[CapabilityBoundingSet]//CAP_/,+}"
			       lsb[SETPRIV_ARGS]+="--bounding-set -all${caps//[$' \r\t\n']} "
			       ;;
			esac
		fi
		if [[ "${service[AmbientCapabilities]+defined}" ]] ; then
			case "${service[AmbientCapabilities]}" in
			    '') lsb[SETPRIV_ARGS]+="--ambient-caps -all ";;
			    ~*) caps="${service[AmbientCapabilities]//CAP_/,-}"
				lsb[SETPRIV_ARGS]+="--ambient-caps +all${caps//[$'~ \r\t\n']} "
				;;
			    *) caps="${service[AmbientCapabilities]//CAP_/,+}"
			       lsb[SETPRIV_ARGS]+="--ambient-caps -all${caps//[$' \r\t\n']} "
			       # TODO: does this require --secbits keep-cap? See
			       # https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#AmbientCapabilities=
			       ;;
			esac
		fi
	fi

	# shebang
	gen_shebang >"${lsb_init_script}"
	# embed source and sha256
	gen_origin >>"${lsb_init_script}"
	# source init-d-script
	gen_init_d_script >>"${lsb_init_script}"

	gen_lsb_header >>"${lsb_init_script}"

	gen_directives >>"${lsb_init_script}"

	gen_functions >>"${lsb_init_script}"

	chmod +x "${lsb_init_script}"
}

