#!/bin/sh /etc/rc.common
START=60
STOP=01
USE_PROCD=1
MAINU=ripe-atlas
MEASU=ripe-atlas
GROUP=ripe-atlas

extra_command "get_key" "Print the public key to register the probe with"
extra_command "probeid" "Print the probe ID"

validate_config()
{
	uci_load_validate 'ripe-atlas' 'ripe-atlas' "${1}" "${2}" \
		'enabled:bool:1' \
		'log_stderr:bool:0' \
		'log_stdout:bool:0' \
		'mode:string:prod' \
		'rxtx_report:bool:0'
}

create_dir()
{
	local user
	local group
	local mode
	local dir
	local ret=0

	user="${1}"
	shift
	group="${1}"
	shift
	mode="${1}"
	shift

	while [ -n "${1}" ]; do
		dir="${1}"
		# The probe user may have put a link in its place.
		[ -L "${dir}" ] && rm -f "${dir}"
		mkdir -p "${dir}" || ret=1
		chown ${user}:${group} "${dir}" || ret=1
		chmod ${mode} "${dir}" || ret=1
		shift
	done

	return ${ret}
}

setup_environment()
{
	local stamp=/var/run/ripe-atlas/spool.done

	create_dir root root 0755 /var/run/ripe-atlas
	create_dir ${MEASU} ${GROUP} 0775 \
		/var/run/ripe-atlas/pids \
		/var/run/ripe-atlas/status

	# Below a spool someone else made, links could be swapped in at any
	# time.
	mkdir -p /var/spool
	if [ -n "$(find /var/spool /var/spool/ripe-atlas -maxdepth 0 \
			! -user 0 2> /dev/null)" ]; then
		logger -t ripe-atlas -p daemon.err \
			'/var/spool/ripe-atlas was not made by root'
		return 1
	fi

	# The probe user owns the spool directories and could swap in a link
	# for root's chown and chmod to follow. Set them up only until that
	# worked once since boot (/var is a tmpfs), so only while no probe
	# runs.
	[ -e "${stamp}" ] && [ -d /var/spool/ripe-atlas ] && return 0

	create_dir ${MEASU} ${GROUP} 2775 \
		/var/spool/ripe-atlas/crons \
		/var/spool/ripe-atlas/crons/main \
		/var/spool/ripe-atlas/crons/2 \
		/var/spool/ripe-atlas/crons/3 \
		/var/spool/ripe-atlas/crons/4 \
		/var/spool/ripe-atlas/crons/5 \
		/var/spool/ripe-atlas/crons/6 \
		/var/spool/ripe-atlas/crons/7 \
		/var/spool/ripe-atlas/crons/8 \
		/var/spool/ripe-atlas/crons/9 \
		/var/spool/ripe-atlas/crons/10 \
		/var/spool/ripe-atlas/crons/11 \
		/var/spool/ripe-atlas/crons/12 \
		/var/spool/ripe-atlas/crons/13 \
		/var/spool/ripe-atlas/crons/14 \
		/var/spool/ripe-atlas/crons/15 \
		/var/spool/ripe-atlas/crons/16 \
		/var/spool/ripe-atlas/crons/17 \
		/var/spool/ripe-atlas/crons/18 \
		/var/spool/ripe-atlas/crons/19 \
		/var/spool/ripe-atlas/crons/20 \
		/var/spool/ripe-atlas/data \
		/var/spool/ripe-atlas/data/new \
		/var/spool/ripe-atlas/data/oneoff \
		/var/spool/ripe-atlas/data/out \
		/var/spool/ripe-atlas/data/out/ooq \
		/var/spool/ripe-atlas/data/out/ooq10 &&
		rm -f "${stamp}" && : > "${stamp}"

	return 0
}

start_instance()
{
	local cfg=/etc/ripe-atlas/config.txt
	local mod=/etc/ripe-atlas/mode
	local key=/etc/ripe-atlas/probe_key

	if [ ${2} -ne 0 ]; then
		echo 'validation failed'
		return 1
	fi

	enabled="$(get_bool "${enabled}" 1)"
	log_stderr="$(get_bool "${log_stderr}" 0)"
	log_stdout="$(get_bool "${log_stdout}" 0)"
	rxtx_report="$(get_bool "${rxtx_report}" 0)"

	if [ ${enabled} -eq 0 ]; then
		return 1
	fi

	case "${mode}" in
		test|dev|prod)
			;;
		*)
			return 1
			;;
	esac

	# Only one probe may present the key: wait until the atlas-sw-probe
	# one is taken over and atlas is disabled. While the key is not taken
	# over, log the wait, as at boot the takeover runs before the log
	# daemon and its errors are lost.
	if [ -e /etc/uci-defaults/80-ripe-atlas ]; then
		if [ ! -s "${key}" ] &&
				{ [ -s /usr/libexec/atlas-probe-scripts/etc/probe_key ] ||
				[ -s /etc/atlas/probe_key ]; }; then
			logger -t ripe-atlas -p daemon.warn \
				'waiting for /etc/uci-defaults/80-ripe-atlas'
		fi
		return 1
	fi

	# ripe-atlas-probe or -anchor brings the rest. apk installs it only after
	# this package started the service, also over an older variant without
	# this file.
	[ -f /usr/share/ripe-atlas/variant ] || return 1

	echo 'Starting RIPE Atlas'
	umask 022
	setup_environment || return 1

	# /etc/ripe-atlas stays root's, so prepare what generic-ATLAS.sh would
	# otherwise write there: the key, and the registration servers of the
	# mode, which it copies only when they are missing.
	if [ ! -s "${key}" ]; then
		# ssh-keygen would ask before replacing an empty file.
		rm -f "${key}" "${key}.pub"
		if ! ssh-keygen -q -t rsa -P '' -C "$(uname -n)" -f "${key}" \
				< /dev/null; then
			logger -t ripe-atlas -p daemon.err "cannot create ${key}"
			return 1
		fi
		logger -t ripe-atlas -p daemon.notice 'created a new probe key'
	fi
	# OpenSSH refuses a private key that others can read.
	chmod 0600 "${key}"
	rm -f /etc/ripe-atlas/reg_servers.sh "${cfg}" "${mod}"
	cp /usr/lib/ripe-atlas/scripts/reg_servers.sh.${mode} \
		/etc/ripe-atlas/reg_servers.sh || return 1
	: > "${cfg}"

	if [ ${rxtx_report} -ne 0 ]; then
		echo 'RXTXRPT=yes' >> "${cfg}"
	fi
	echo "${mode}" > "${mod}"

	procd_open_instance
	procd_set_param command /usr/sbin/ripe-atlas
	procd_set_param group ${GROUP}
	procd_set_param stdout ${log_stdout}
	procd_set_param stderr ${log_stderr}
	procd_set_param respawn
	if [ -x /sbin/ujail ] && user_exists ${MAINU}; then
		chown ${MAINU}:${GROUP} "${key}" "${key}.pub"
		procd_set_param user ${MAINU}
		procd_add_jail ripe-atlas
		procd_append_param capabilities /usr/share/ripe-atlas/capabilities.json
	else
		# procd grants capabilities only through ujail, and without
		# CAP_NET_RAW ping and traceroute fail. Run as root, like
		# atlas-sw-probe did; the group still tags the probe processes.
		logger -t ripe-atlas -p daemon.warn \
			"ujail or user ${MAINU} missing, running the probe as root"
	fi
	procd_close_instance

	return 0
}

start_service()
{
	config_load 'ripe-atlas'
	config_foreach validate_config 'ripe-atlas' start_instance
}

stop_service()
{
	local pids
	local pid
	local procs

	# Up to OpenWrt 25.12, procd signals only the main script; the daemons,
	# the ssh tunnel and telnetd it started would keep running. Take them
	# from the instance's cgroup, or, when that holds none, from the group
	# only the probe uses.
	for procs in /sys/fs/cgroup/services/ripe-atlas/*/cgroup.procs; do
		[ -f "${procs}" ] || continue
		while read -r pid; do
			pids="${pid} ${pids}"
		done < "${procs}"
	done
	if [ -z "${pids}" ]; then
		for pid in $(find /proc -maxdepth 1 -type d -group ${GROUP} \
				2> /dev/null); do
			pids="${pid##*/} ${pids}"
		done
	fi

	if [ -n "${pids}" ]; then
		kill ${pids} 1>/dev/null 2>&1
	fi
}

# procd would not restart the probe when its options change, and up to
# OpenWrt 25.12, when it stops the instance, it signals only the main
# script.
reload_service()
{
	stop
	start
}

registered_id()
{
	awk '$1 == "PROBE_ID" { print $2 }' \
		/var/run/ripe-atlas/status/reg_init_reply.txt 2> /dev/null
}

get_key()
{
	local pub=/etc/ripe-atlas/probe_key.pub
	local variant
	local id

	variant="$(cat /usr/share/ripe-atlas/variant 2> /dev/null)"
	if [ -z "${variant}" ]; then
		echo 'Install ripe-atlas-probe or ripe-atlas-anchor first' >&2
		return 1
	fi
	if [ ! -s "${pub}" ]; then
		echo "${pub} is missing, start the probe to create it" >&2
		return 1
	fi
	id="$(registered_id)"
	if [ -n "${id}" ]; then
		echo "Registered as probe ${id} with:"
	elif [ "${variant}" = anchor ]; then
		echo 'Register the anchor at https://atlas.ripe.net/anchors/apply/ with:'
	else
		echo 'Register the probe at https://atlas.ripe.net/apply/swprobe/ with:'
	fi
	cat "${pub}"
}

probeid()
{
	local id

	id="$(registered_id)"
	if [ -z "${id}" ]; then
		echo 'The probe has not registered since boot' >&2
		return 1
	fi
	echo "Probe ID is ${id}"
}

service_triggers()
{
	procd_add_reload_trigger 'ripe-atlas'
	procd_add_validation validate_config
}
