#!/bin/sh
# DPHMS OS — container-side desktop launcher (v40: machine-id sanitizer + v27 Xvnc)
# Starts Xvnc DIRECTLY — no vncserver wrapper, no xauth, no lock files.
# Why: Android's /data filesystem denies hard link(2) (the same reason
# the rootfs tar spewed terminfo link warnings during extraction).
# Xvnc's lock-file mechanism AND xauth's authority locking are both
# link()-based, so the vncserver wrapper can never work here — it dies
# with "Linking lock file /tmp/.X1-lock in place failed: Permission
# denied" and "xauth: error in locking authority file /root/.Xauthority".
# -nolock disables X's lock mechanism; -ac disables X access control
# (VNC password via -rfbauth still protects the desktop; -localhost
# keeps the socket on 127.0.0.1 — same security posture as before).
mkdir -p /tmp/.X11-unix /tmp/.ICE-unix
chmod 1777 /tmp /tmp/.X11-unix /tmp/.ICE-unix 2>/dev/null
rm -f /tmp/.X1-lock /tmp/.X11-unix/X1

VNC=/root/.vnc
mkdir -p "$VNC"
# Do not wipe old evidence before the already-running guard.

PV=$(cat /usr/local/share/dphms/payload-version 2>/dev/null | tr -d "[:space:]"); PV=${PV:-?}
# already running? (double-tap START guard)
# v27: only trust "already running" when the WHOLE desktop is alive (X server + window manager + panel +
# a VNC that is actually listening). A half-dead leftover from an older payload used to be reported as
# running, so a new payload never took effect until a manual STOP. Now a half-dead session is torn down
# and relaunched with the current scripts.
if [ -f "$VNC/xvnc.pid" ] && kill -0 "$(cat "$VNC/xvnc.pid")" 2>/dev/null; then
  if pgrep -x xfwm4 >/dev/null 2>&1 && pgrep -x xfce4-panel >/dev/null 2>&1 && grep -q 'Session v' "$VNC/session.log" 2>/dev/null; then
    echo "[!] Desktop already running (payload v$PV) - VNC on 127.0.0.1:5901 (password: dphms)"
    echo "── session log (last 25 lines) ──"
    tail -25 "$VNC/session.log" 2>/dev/null
    echo "── xvnc log (last 10 lines) ──"
    tail -10 "$VNC/xvnc.log" 2>/dev/null
    exit 0
  fi
  echo "[*] A desktop from an older or half-dead session is running - restarting it with the current scripts."
  sh /usr/local/bin/dphms-stop 2>&1 | tail -1
  sleep 2
fi

: > "$VNC/session.log"
# v28: repair DNS every start and record a network check (printed in the console below)
[ -x /usr/local/bin/dphms-net ] && /usr/local/bin/dphms-net > "$VNC/net.log" 2>&1 &
NETPID=$!
# v29: audio server inside the container (or detect the phone's), so apps and the volume plugin stop erroring
[ -x /usr/local/bin/dphms-audio ] && /usr/local/bin/dphms-audio > "$VNC/audio.log" 2>&1 &
AUDPID=$!
# password file must exist — regenerate if a fresh install missed it
if [ ! -s "$VNC/passwd" ]; then
  echo "dphms" | vncpasswd -f > "$VNC/passwd" 2>/dev/null || true
  chmod 600 "$VNC/passwd" 2>/dev/null
fi

# v27: desktop size. Default 1280x720 is landscape, so on a portrait phone AVNC letterboxes it to ~90% width
# and ~22% height. Pick the size in /sdcard/Download/dphms-resolution.txt (e.g. 720x1600 portrait, 1280x720
# landscape) or let it default to 1080x2400 (20:9), the native screen of the Moto G Power 5G (2024). Put 810x1440 in that file if it feels slow.
GEOM=1080x2400
RES=""
for f in /sdcard/Download/dphms-resolution.txt /root/.vnc/resolution; do [ -r "$f" ] && RES=$(tr -d ' \r\n' < "$f" 2>/dev/null) && break; done
case "$RES" in [0-9][0-9][0-9]x[0-9][0-9][0-9]|[0-9][0-9][0-9][0-9]x[0-9][0-9][0-9]|[0-9][0-9][0-9]x[0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9]x[0-9][0-9][0-9][0-9]) GEOM=$RES ;; esac
echo "$GEOM" > "$VNC/geometry"
echo "[*] Desktop size: $GEOM (override: put e.g. 1280x720 in Downloads/dphms-resolution.txt)"
Xvnc :1 -geometry $GEOM -depth 24 -nolock -ac -localhost \
  -rfbauth "$VNC/passwd" -rfbport 5901 -pn \
  > "$VNC/xvnc.log" 2>&1 &
XPID=$!
echo "$XPID" > "$VNC/xvnc.pid"

# Wait on the server process, not solely the filesystem socket: abstract X may work without a file.
sleep 2
if ! kill -0 "$XPID" 2>/dev/null; then tail -30 "$VNC/xvnc.log"; exit 1; fi
# wait for the X socket to come up (max ~10s)
i=0
while [ $i -lt 20 ]; do
  [ -e /tmp/.X11-unix/X1 ] && break
  sleep 0.5
  i=$((i+1))
done

# paint the DPHMS root grey IMMEDIATELY — if AVNC shows grey but no desktop,
# the framebuffer works and the session died (vs. black = no VNC updates at all)
DISPLAY=:1 xsetroot -solid "#1E1E1E" >> "$VNC/session.log" 2>&1

# session env hardening: xfce4-session wants a writable XDG_RUNTIME_DIR.
# v20: the XDG dir is now ALSO the dbus socket home — the stock session
# config listens on unix:tmpdir=/tmp and dbus kept getting EACCES binding
# there under proot, killing the whole XFCE session (the black screen).
export XDG_RUNTIME_DIR=/tmp/xdg-runtime
mkdir -p "$XDG_RUNTIME_DIR"
chmod 700 "$XDG_RUNTIME_DIR" 2>/dev/null
rm -f "$XDG_RUNTIME_DIR/bus"
ls -ld /tmp /tmp/.X11-unix "$XDG_RUNTIME_DIR" >> "$VNC/session.log" 2>&1

# launch the DPHMS XFCE session (themed via /root/.xinitrc)
# v21: the bus now listens on an ABSTRACT socket (kernel memory, no filesystem
# bind). v20 proved every filesystem socket bind EACCESes under proot on this
# phone — including our custom XDG path. dbus-run-session stays alive while
# the session runs, so "process died within 4s" = bus failed.
# v40: machine-id sanitizer (container side, BEFORE any dbus-run-session).
# dbus-daemon DIES on an invalid /etc/machine-id or /var/lib/dbus/machine-id
# ("Invalid machine ID"). If a symlink/proot-bind made the host-side pass fail,
# fix it HERE: unlink first (needs dir write, not file write), then write real
# files. If even unlink fails, the invalid file stays but we report it; if a
# file is absent dbus happily generates a random UUID, so absence is safe.
VMID=$(cat /usr/local/share/dphms/machine-id 2>/dev/null | tr -d ' \r\n')
case "$VMID" in
  ????????????????????????????????) ;;
  *) VMID=$(sed 's/-//g' /proc/sys/kernel/random/boot_id 2>/dev/null | tr -d ' \r\n') ;;
esac
case "$VMID" in
  ????????????????????????????????) ;;
  *) VMID=$(od -An -tx1 -N16 /dev/urandom 2>/dev/null | tr -d ' \r\n') ;;
esac
for F in /etc/machine-id /var/lib/dbus/machine-id; do
  rm -f "$F" 2>/dev/null
done
mkdir -p /var/lib/dbus 2>/dev/null
if printf '%s\n' "$VMID" > /etc/machine-id 2>/dev/null; then
  chmod 644 /etc/machine-id 2>/dev/null
  echo "[*] v40: container /etc/machine-id set ($VMID)"
else
  echo "[!] v40: container /etc/machine-id unwritable (proot bind?) - removed, dbus will use a random UUID"
fi
if printf '%s\n' "$VMID" > /var/lib/dbus/machine-id 2>/dev/null; then
  chmod 644 /var/lib/dbus/machine-id 2>/dev/null
  echo "[*] v40: container /var/lib/dbus/machine-id set"
else
  echo "[!] v40: container /var/lib/dbus/machine-id unwritable - removed"
fi

SESS_LAUNCHED=0
if [ -r /usr/local/share/dphms/dbus-session.conf ] && command -v dbus-run-session >/dev/null 2>&1; then
  echo "[*] Session bus via custom config (abstract socket)."
  DISPLAY=:1 HOME=/root USER=root dbus-run-session --config-file=/usr/local/share/dphms/dbus-session.conf -- sh /usr/local/bin/dphms-session >> "$VNC/session.log" 2>&1 &
  BUSPID=$!
  sleep 4
  if ! kill -0 "$BUSPID" 2>/dev/null; then
    echo "[!] Custom bus died — retrying with the stock dbus config." >> "$VNC/session.log" 2>&1
    DISPLAY=:1 HOME=/root USER=root dbus-run-session -- sh /usr/local/bin/dphms-session >> "$VNC/session.log" 2>&1 &
    BUSPID=$!
    sleep 4
    if ! kill -0 "$BUSPID" 2>/dev/null; then
      echo "[!] Stock config failed too — launching without a session bus (partial session expected)." >> "$VNC/session.log" 2>&1
      DISPLAY=:1 HOME=/root USER=root sh /usr/local/bin/dphms-session >> "$VNC/session.log" 2>&1 &
      SESS_LAUNCHED=1
    fi
  fi
elif command -v dbus-run-session >/dev/null 2>&1; then
  echo "[*] Launching session via dbus-run-session (stock config)."
  DISPLAY=:1 HOME=/root USER=root dbus-run-session -- sh /usr/local/bin/dphms-session >> "$VNC/session.log" 2>&1 &
else
  echo "[!] dbus-run-session missing — launching without a session bus (likely session failure)."
  DISPLAY=:1 HOME=/root USER=root sh /usr/local/bin/dphms-session >> "$VNC/session.log" 2>&1 &
fi
[ -x /usr/bin/xfce4-session ] || echo "[!] xfce4-session binary NOT FOUND — desktop packages incomplete."

echo "[*] VNC started (payload v$PV). Connect AVNC to 127.0.0.1:5901, password dphms."

# ── diagnostics: show us (in the app console) why the session drew or died ──
sleep 22
# v28: network check result (DNS + HTTPS test from inside the container)
wait $NETPID 2>/dev/null
cat "$VNC/net.log" 2>/dev/null
wait $AUDPID 2>/dev/null
cat "$VNC/audio.log" 2>/dev/null
echo "── session log (last 25 lines) ──"
tail -25 "$VNC/session.log" 2>/dev/null
echo "── xvnc log (last 10 lines) ──"
tail -10 "$VNC/xvnc.log" 2>/dev/null
echo "── process check ──"
echo "── icon loader check ──"
grep -c 'Loader process exited early' "$VNC/session.log" 2>/dev/null | sed 's/^/  glycin loader failures in session.log: /'
for p in Xvnc xfwm4 xfce4-panel xfdesktop xfce4-terminal; do pgrep -x "$p" >/dev/null 2>&1 && echo "  UP   $p" || echo "  DOWN $p"; done
