#!/bin/bash
# Run GNOME Shell headless + gnome-remote-desktop on a private D-Bus session,
# so the Praya extension can be tested from an RDP client.
#
# GNOME 50 removed `gnome-shell --nested`; its replacement, `--devkit`, needs
# /usr/libexec/mutter-devkit, which Debian/BlankOn does not ship. This is the
# workaround: a headless shell you view over RDP.
#
# Invoked by `make run`, which wraps it in dbus-run-session.
set -e

RDP_PORT="${RDP_PORT:-3390}"
RDP_USER="${RDP_USER:-praya}"
RDP_PASS="${RDP_PASS:-praya}"
RDP_SIZE="${RDP_SIZE:-1920x980}"
# Set RDP_CLIENT=0 to only serve, and connect yourself (Remmina, another host).
RDP_CLIENT="${RDP_CLIENT:-1}"
CERT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/praya-test-rdp"
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
# The uuid depends on the variant (PRAYA_GENERIC). Allow an override.
UUID="${UUID:-$("$REPO_DIR/tools/uuid.sh")}"

# Run the extension straight from the working tree, without installing it
# into ~/.local/share. See tools/dev-datahome.sh for the details.
source "$REPO_DIR/tools/dev-datahome.sh"

# grdctl writes *global* per-user configuration, which a running daemon watches.
# A second instance would therefore reconfigure the first one out from under
# itself and kill the session you are using. Take a lock instead.
#
# The fd is allocated through {LOCKFD} rather than a fixed `exec 9>`: bash
# marks those close-on-exec, so processes spawned from the test session (an
# app launched inside it, say) do not inherit the lock and keep it held after
# this script has exited.
LOCK="${XDG_RUNTIME_DIR:-/tmp}/praya-run-rdp.lock"
exec {LOCKFD}>"$LOCK"
if ! flock -n "$LOCKFD"; then
	echo "!!! Another $(basename "$0") is already running."
	echo "!!! Stop it first - gnome-remote-desktop config is per-user and global,"
	echo "!!! so a second session would break the one you are already using."
	echo "!!! If nothing is running, see who holds the lock with:"
	echo "!!!     fuser -v $LOCK"
	exit 1
fi

# gnome-remote-desktop refuses to start its RDP backend without a TLS cert.
if [ ! -f "$CERT_DIR/rdp.crt" ] || [ ! -f "$CERT_DIR/rdp.key" ]; then
	echo "==> Generating self-signed TLS certificate in $CERT_DIR"
	mkdir -p "$CERT_DIR"
	openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 \
		-subj "/C=ID/ST=./L=./O=praya-test/CN=localhost" \
		-keyout "$CERT_DIR/rdp.key" -out "$CERT_DIR/rdp.crt" >/dev/null 2>&1
	chmod 600 "$CERT_DIR/rdp.key"
fi

# Filter gnome-remote-desktop's TPM/FreeRDP startup noise.
grd_quiet() { grdctl "$@" 2>&1 | grep -viE "Init TPM credentials|freerdp|certificate is invalid" || true; }

echo "==> Configuring gnome-remote-desktop (port $RDP_PORT, user $RDP_USER)"
grd_quiet --headless rdp set-tls-cert "$CERT_DIR/rdp.crt"
grd_quiet --headless rdp set-tls-key "$CERT_DIR/rdp.key"
grd_quiet --headless rdp set-port "$RDP_PORT"
grd_quiet --headless rdp disable-port-negotiation
grd_quiet --headless rdp set-credentials "$RDP_USER" "$RDP_PASS"
grd_quiet --headless rdp enable

# `grdctl enable` D-Bus-activates the user's own gnome-remote-desktop units.
# Those belong to the real desktop session and would steal $RDP_PORT, so the
# daemon we start below could not bind it. Stop them and wait for the release.
systemctl --user stop gnome-remote-desktop-headless.service \
	gnome-remote-desktop.service >/dev/null 2>&1 || true
# Also reap a daemon left behind by a previous run that did not exit cleanly.
pkill -u "$USER" -f "gnome-remote-desktop-daemon --headless --rdp-port $RDP_PORT" \
	>/dev/null 2>&1 || true
for _ in $(seq 20); do
	ss -ltn 2>/dev/null | grep -q ":$RDP_PORT " || break
	sleep 0.25
done

SHELL_PID=""
GRD_PID=""
CLIENT_PID=""
cleanup() {
	trap - EXIT INT TERM
	echo
	echo "==> Shutting down"
	[ -n "$CLIENT_PID" ] && kill "$CLIENT_PID" 2>/dev/null
	[ -n "$GRD_PID" ] && kill "$GRD_PID" 2>/dev/null
	[ -n "$SHELL_PID" ] && kill "$SHELL_PID" 2>/dev/null
	wait 2>/dev/null
	exit 0
}
trap cleanup EXIT INT TERM

echo "==> Starting headless GNOME Shell"
gnome-shell --headless --wayland &
SHELL_PID=$!
sleep 5

echo "==> Starting gnome-remote-desktop"
/usr/libexec/gnome-remote-desktop-daemon --headless --rdp-port "$RDP_PORT" &
GRD_PID=$!
sleep 3

# Wait for the RDP server to actually accept connections before dialling it.
for _ in $(seq 40); do
	ss -ltn 2>/dev/null | grep -q ":$RDP_PORT " && break
	sleep 0.25
done

if [ "$RDP_CLIENT" = "1" ] && command -v xfreerdp3 >/dev/null 2>&1; then
	echo "==> Connecting with xfreerdp3 ($RDP_SIZE)"
	xfreerdp3 "/v:localhost:$RDP_PORT" "/u:$RDP_USER" "/p:$RDP_PASS" \
		"/size:$RDP_SIZE" /cert:ignore &
	CLIENT_PID=$!
fi

cat <<MSG

=====================================================================
 RDP server on localhost:$RDP_PORT  --  user $RDP_USER / password $RDP_PASS

 Reconnect (or connect from elsewhere) with:
   xfreerdp3 /v:localhost:$RDP_PORT /u:$RDP_USER /p:$RDP_PASS \\
             /size:$RDP_SIZE /cert:ignore
   remmina  -> server localhost:$RDP_PORT, resolution $RDP_SIZE

 Closing the client window leaves the server up, so you can reconnect.
 Ctrl+C here stops the shell, the RDP server and the client together.
=====================================================================

MSG

# If either process dies, fall through to cleanup and take the other with it.
wait -n "$SHELL_PID" "$GRD_PID"
