#!/bin/sh
#cpsv - utility to install and manage runscripts

# Copyright: 2022 Lorenzo Puliti <plorenzo@disroot.org>
# License: BSD-3-clause
set -e

err() { >&2 printf '%s\n\n' "$*"; exit 1; }
fatal() { err "${0##*/}: fatal: $*"; }
warn() { >&2 printf '%s\n' "${0##*/}: warning: $*"; }
usage() {
  err "Usage: ${0##*/} [ -f ] -a  <service> [<service-2> <service-3> ... ]
       ${0##*/} -d  <service>
       ${0##*/} [ -f ] --sync
       ${0##*/} --list"
}

if [ $# -eq 0 ]; then
	warn "wrong syntax"  && usage
fi

rcode=0
runitsvdir=$CPSV_DIR
test -n "$runitsvdir" || runitsvdir=/etc/sv
test -d "$runitsvdir" || fatal "${0##*/} $runitsvdir : not a directory"
sysvdir=/etc/init.d
systemdsvdir=/lib/systemd/system
systemdetcdir=/etc/systemd/system

sv_diff() {
	service="$1"
	if [ -e "/usr/share/runit/meta/$service/logscript" ]; then
		diff -Naur --exclude=supervise --exclude=log "/usr/share/runit/sv/$service" "$runitsvdir/$service"
	else
		diff -Naur --exclude=supervise "/usr/share/runit/sv/$service" "$runitsvdir/$service"
	fi
	return
}

is_stock() {
	sv_diff "$service" >/dev/null
}

cp_sv() {
	service="$1"
	if [ -d "$runitsvdir/$service" ]; then
		if ! is_stock ; then
			if [ -n "$force" ]; then
				cp -a "/usr/share/runit/sv/$service" "$runitsvdir/"
				/lib/runit/make_svlinks "$service"
			else
				warn "skipping $service, local version exists, use -f to overwrite" \
				&& rcode=$((rcode+1))
			fi
		fi
	else
		cp -a "/usr/share/runit/sv/$service" "$runitsvdir/"
		/lib/runit/make_svlinks "$service"
	fi
}

loop_allsv() {
	for dir in /usr/share/runit/sv/* ; do
		stocksv=$(basename "$dir")
		if [ -e "$systemdsvdir/$stocksv.service" ] || [ -e "$sysvdir/$stocksv" ]; then
			#installed: false positives for sysv are possible (purged instead of installed)
			if [ -n "$sync" ]; then
				cp_sv "$stocksv"
			else
				list_sv "$stocksv"
			fi
		else
			if [ -d "$runitsvdir/$stocksv" ] && [ ! -e "$systemdetcdir/$stocksv.service" ]; then
				#purged: inaccurate for sysv only services
				if [ -n "$list" ]; then
					list_sv "$stocksv" '[p]'
				fi
			fi
		fi
	done
}

list_sv() {
	service="$1"
	stat="$2"
	if [ -z "$stat" ]; then
		if [ ! -d "$runitsvdir/$service" ]; then
			stat='[a]'
		else
			stat='[i]'
			if ! is_stock ; then
				stat='[l]'
			fi
		fi
	fi
	echo "$stat: $service"
}

while [ $# -gt 0 ]; do
	case $1 in
		-a)
		test "$(id -u)" = 0 || fatal "${0##*/} -a must be run by root."
		add=1
		opt=1
		shift
		;;
		-f)
		force=1
		opt=1
		shift
		;;
		-af|-fa)
		test "$(id -u)" = 0 || fatal "${0##*/} -a must be run by root."
		add=1
		force=1
		opt=1
		shift
		;;
		-d)
		diff=1
		opt=1
		shift
		;;
		--list)
		list=1
		opt=1
		shift
		;;
		--sync)
		test "$(id -u)" = 0 || fatal "${0##*/} --sync must be run by root."
		sync=1
		opt=1
		shift
		;;
		-r|--run)
		#TODO: needs new service layout in runit
		run=1
		shift
		;;
		-u|--user)
		#TODO: user instances, needs support in runit, see mk-runscript
		shift
		user="${1}"
		shift
		;;
		-u=*|--user=*)
		#TODO: user instances, needs support in runit, see mk-runscript
		user="${1#*=}"
		shift
		;;
		-*|--*)
		warn "unknown option $1" && usage
		;;
		*)
		[ -z "$opt" ] && warn "wrong syntax" && usage
		break
		;;
	esac
done

#syntax check
if [ -n "$1" ]; then
	[ -n "$list" ] && warn "wrong syntax"  && usage
	[ -n "$sync" ] && warn "wrong syntax"  && usage
fi
#check option combo
if [ -n "$add" ]; then
	[ -n "$diff" ] && warn "wrong syntax"  && usage
	[ -n "$list" ] && warn "wrong syntax"  && usage
	[ -n "$sync" ] && warn "wrong syntax"  && usage
fi
if [ -n "$diff" ]; then
	[ -n "$list" ] && warn "wrong syntax"  && usage
	[ -n "$sync" ] && warn "wrong syntax"  && usage
	[ -n "$force" ] && warn "wrong syntax"  && usage
	[ ! -d /usr/share/runit/sv/"$1" ] && fatal "no stock service found for $1"
	[ -n "$2" ] && warn "only one service is expected"  && usage
	sv_diff "$1"
	exit
fi
if [ -n "$list" ]; then
	[ -n "$diff" ] && warn "wrong syntax"  && usage
	[ -n "$sync" ] && warn "wrong syntax"  && usage
	[ -n "$force" ] && warn "wrong syntax"  && usage
	loop_allsv
	exit 0
fi
if [ -n "$sync" ]; then
	loop_allsv
	exit "$rcode"
fi

for arg in "$@"; do
	if [ ! -d /usr/share/runit/sv/"$arg" ]; then
		warn "no stock service found for $arg"
		rcode=$((rcode+1))
	else
		cp_sv "$arg"
	fi
	shift
done

exit "$rcode"
