#!/bin/sh
# Grant the DockBeacon service user read access to PM2 log directories.
# Safe to re-run. Does not change who owns PM2 and does not chmod logs world-readable.
#
#   sudo /usr/lib/docker-log-viewer/pm2-grant-access
#
# Finds running PM2 God Daemons. Also honors PM2_HOME / PM2_USER in
# /etc/docker-log-viewer/docker-log-viewer.env.
set -eu

USER_NAME=${PM2_GRANT_USER:-docker-log-viewer}
ENV_FILE=${PM2_ENV_FILE:-/etc/docker-log-viewer/docker-log-viewer.env}
SUDOERS_SRC=/usr/lib/docker-log-viewer/docker-log-viewer-pm2.sudoers
SUDOERS_DST=/etc/sudoers.d/docker-log-viewer-pm2

if [ "$(id -u)" -ne 0 ]; then
	echo "Run as root: sudo /usr/lib/docker-log-viewer/pm2-grant-access" >&2
	exit 1
fi
if ! id "$USER_NAME" >/dev/null 2>&1; then
	echo "User $USER_NAME does not exist. Install the docker-log-viewer package first." >&2
	exit 1
fi
if ! command -v setfacl >/dev/null 2>&1; then
	echo "setfacl is required (package: acl)." >&2
	exit 1
fi

install_sudoers() {
	[ -f "$SUDOERS_SRC" ] || return 0
	if [ -f "$SUDOERS_DST" ] && cmp -s "$SUDOERS_SRC" "$SUDOERS_DST"; then
		return 0
	fi
	if command -v visudo >/dev/null 2>&1; then
		visudo -cf "$SUDOERS_SRC" >/dev/null
	fi
	install -m 0440 -o root -g root "$SUDOERS_SRC" "$SUDOERS_DST"
	echo "Installed $SUDOERS_DST"
}

env_value() {
	key=$1
	[ -f "$ENV_FILE" ] || return 0
	# Last assignment wins. Do not source the file.
	grep -E "^${key}=" "$ENV_FILE" 2>/dev/null | tail -n 1 | cut -d= -f2- | tr -d '"' | tr -d "'"
}

other_can_traverse() {
	dir=$1
	perm=$(stat -c %A "$dir" 2>/dev/null || true)
	# ----------x is the 10th character of stat -c %A (1-based).
	[ "$(printf '%s' "$perm" | cut -c 10)" = "x" ]
}

grant_traverse() {
	dir=$1
	while [ -n "$dir" ] && [ "$dir" != "/" ]; do
		if [ -d "$dir" ] && ! other_can_traverse "$dir"; then
			setfacl -m "u:${USER_NAME}:x" "$dir"
		fi
		next=$(dirname "$dir")
		[ "$next" = "$dir" ] && break
		dir=$next
	done
}

grant_logs() {
	home=$1
	[ -d "$home" ] || return 0
	grant_traverse "$home"
	logs="$home/logs"
	if [ ! -d "$logs" ]; then
		echo "No log directory yet: $logs"
		return 0
	fi
	setfacl -m "u:${USER_NAME}:rx" "$logs"
	setfacl -d -m "u:${USER_NAME}:r" "$logs"
	# Existing files. PM2 names them <app>-out.log and <app>-error.log.
	for f in "$logs"/*-out.log "$logs"/*-error.log; do
		[ -f "$f" ] || continue
		setfacl -m "u:${USER_NAME}:r" "$f"
	done
	echo "Granted $USER_NAME read on $logs"
}

# The packaged unit sets NoNewPrivileges=true, which makes sudo refuse to run.
# PM2 status and start/stop/restart go through the sudo helpers, so lift it
# with a drop-in on hosts where this grant is run.
install_unit_dropin() {
	dir=/etc/systemd/system/docker-log-viewer.service.d
	dst="$dir/pm2.conf"
	want='[Service]
NoNewPrivileges=false'
	if [ -f "$dst" ] && [ "$(cat "$dst")" = "$want" ]; then
		return 0
	fi
	mkdir -p "$dir"
	printf '%s\n' "$want" > "$dst"
	chmod 0644 "$dst"
	if command -v systemctl >/dev/null 2>&1; then
		systemctl daemon-reload >/dev/null 2>&1 || true
	fi
	echo "Installed $dst. Restart: systemctl restart docker-log-viewer"
}

install_sudoers

homes_file=$(mktemp)
trap 'rm -f "$homes_file"' EXIT

add_home() {
	h=$1
	[ -n "$h" ] || return 0
	case "$h" in
		/*) ;;
		*) return 0 ;;
	esac
	if grep -Fxq "$h" "$homes_file" 2>/dev/null; then
		return 0
	fi
	printf '%s\n' "$h" >> "$homes_file"
}

for cmd in /proc/[1-9]*/cmdline; do
	[ -r "$cmd" ] || continue
	text=$(tr '\0' ' ' < "$cmd" 2>/dev/null || true)
	case "$text" in
		*"God Daemon ("*)
			home=$(printf '%s\n' "$text" | sed -n 's/.*God Daemon (\([^)]*\)).*/\1/p')
			add_home "$home"
			;;
	esac
done

add_home "$(env_value PM2_HOME)"
pm2_user=$(env_value PM2_USER)
if [ -n "$pm2_user" ]; then
	if [ "$pm2_user" = "root" ]; then
		add_home /root/.pm2
	else
		add_home "/home/$pm2_user/.pm2"
	fi
fi

if [ ! -s "$homes_file" ]; then
	echo "No PM2 daemon found. Start PM2, or set PM2_HOME / PM2_USER, then re-run this command."
	exit 0
fi

install_unit_dropin

while IFS= read -r home; do
	[ -n "$home" ] || continue
	grant_logs "$home"
done < "$homes_file"
