#!/bin/bash
set -euo pipefail

# This systemd.generator(7) detects if SELinux is running and if the
# user requested an autorelabel. If so, services will be enabled to
# run after subvolumes and partitions are mounted before local-fs.target
# is reached.

# If invoked with no arguments (for testing) write to /tmp.
generatordir="/tmp"
if [ -n "${1-}" ]; then
	generatordir="$1"
fi

enable_units() {
	mkdir -p "${generatordir}"/local-fs.target.requires

	relabel_unit_list=""

	while read -r realdir; do
		# Skip non-fs (swap) mounts, /, /var, /etc (already done in the initrd) and mountpoints with noauto
		if [ "${realdir:0:1}" != "/" ] \
		   || [ "${realdir}" = "/" ] || [ "${realdir}" = "/var" ] || [ "${realdir}" = "/etc" ] \
		   || ! findmnt --fstab --noheadings --output FSTYPE --target "${realdir}" | grep -qE '^(ext2|ext3|ext4|xfs|btrfs|jfs)$' \
		   || findmnt --fstab --noheadings --output OPTIONS --target "${realdir}" | grep -qwE 'noauto|x-systemd\.automount|_netdev'; then
			continue
		fi

		mountunit=$(systemd-escape --path "${realdir}")
		unitfile="${mountunit}-relabel.service"
		relabel_unit_list="$unitfile $relabel_unit_list"

		opts="-T 0"
		[ "${realdir}" == "/.snapshots" ] && opts="${opts} -x"

		cat >"${generatordir}/${unitfile}" <<-EOF
			[Unit]
			Description=Relabel ${realdir}
			DefaultDependencies=no
			Requires=systemd-tmpfiles-setup-sys.service
			After=systemd-tmpfiles-setup-sys.service
			RequiresMountsFor=${realdir}
			Before=local-fs.target
			ConditionSecurity=selinux

			[Service]
			Type=oneshot
			ExecStart=/sbin/restorecon -R ${opts} ${realdir}
			RemainAfterExit=true
		EOF

		ln -sf ../"${unitfile}" "${generatordir}"/local-fs.target.requires/"${unitfile}"
	done < <(findmnt --fstab --noheadings --output TARGET)

	unitfile="mark-autorelabel-done.service"
	cat >"${generatordir}/${unitfile}" <<-EOF
		[Unit]
		Description=Mark autorelabel as done
		DefaultDependencies=no
		Before=local-fs.target
		After=${relabel_unit_list}
		Requires=${relabel_unit_list}
		ConditionSecurity=selinux
		ConditionPathExists=/etc/selinux/.autorelabel

		[Service]
		Type=oneshot
		ExecStart=/usr/bin/rm /etc/selinux/.autorelabel
		RemainAfterExit=true
	EOF

	ln -sf "../${unitfile}" "${generatordir}/local-fs.target.requires/${unitfile}"
}

if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then
	if [ -f /etc/selinux/.autorelabel ] || grep -wq autorelabel /proc/cmdline; then
		enable_units
	fi
fi
