#!/bin/sh
# walnut-bugreport: collect system diagnostics for Walnut Linux bug reports.
#
# Collects hardware, kernel, boot, and package status, redacting private
# network information (IPv4 addresses except 127.0.0.1, MAC addresses),
# hostnames, and user names in file paths.
set -eu

ROOT="${WALNUT_ROOT_PREFIX:-}"
if [ $# -gt 0 ]; then
	OUT="$1"
else
	OUT="./walnut-bugreport-$(date +%Y%m%d-%H%M%S).txt"
fi

TMP="$(mktemp "${TMPDIR:-/tmp}/walnut-bugreport.XXXXXX")"
trap 'rm -f "$TMP"' EXIT INT TERM

# Identify hostname and user for redaction
HOST="${WALNUT_TEST_HOST:-$(hostname 2>/dev/null || cat "${ROOT}/etc/hostname" 2>/dev/null || uname -n 2>/dev/null || true)}"
CUR_USER="${WALNUT_TEST_USER:-${SUDO_USER:-${USER:-$(id -un 2>/dev/null || true)}}}"

section() {
	printf '\n=== %s ===\n' "$1" >> "$TMP"
}

# 1. Date
section "Date"
date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null >> "$TMP" || date >> "$TMP" 2>&1 || echo "date failed" >> "$TMP"

# 2. Kernel (uname -a)
section "Kernel (uname -a)"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_uname" ]; then
	cat "${ROOT}/fake_uname" >> "$TMP" 2>&1
else
	uname -a >> "$TMP" 2>&1 || echo "uname failed" >> "$TMP"
fi

# 3. OS Release (/etc/os-release)
section "OS Release (/etc/os-release)"
if [ -f "${ROOT}/etc/os-release" ]; then
	cat "${ROOT}/etc/os-release" >> "$TMP" 2>&1
else
	echo "Not found" >> "$TMP"
fi
# the release it was installed from (the image build's)
[ -f "${ROOT}/etc/walnut-image" ] && grep -v '^#' "${ROOT}/etc/walnut-image" >> "$TMP" 2>&1

# 4. Installed Walnut / PS3 packages (dpkg-query)
section "Packages (dpkg-query)"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_dpkg" ]; then
	cat "${ROOT}/fake_dpkg" >> "$TMP" 2>&1
elif command -v dpkg-query >/dev/null 2>&1; then
	dpkg-query -W -f='${Package}\t${Version}\t${Status}\n' \
		'ps3-*' 'walnut*' 'linux-image-*' 'ps3rsx-*' 'mesa-*' 2>/dev/null >> "$TMP" || true
else
	echo "dpkg-query not available" >> "$TMP"
fi

# 5. CPU Info (/proc/cpuinfo)
section "CPU Info (/proc/cpuinfo)"
if [ -f "${ROOT}/proc/cpuinfo" ]; then
	cat "${ROOT}/proc/cpuinfo" >> "$TMP" 2>&1
else
	echo "Not found" >> "$TMP"
fi

# 6. Console Model (/proc/device-tree)
section "Console Model"
if [ -f "${ROOT}/proc/device-tree/model" ]; then
	printf 'Model: ' >> "$TMP"
	tr '\0' '\n' < "${ROOT}/proc/device-tree/model" 2>/dev/null | head -n 1 >> "$TMP"
elif [ -f "${ROOT}/proc/device-tree/compatible" ]; then
	printf 'Compatible: ' >> "$TMP"
	tr '\0' '\n' < "${ROOT}/proc/device-tree/compatible" 2>/dev/null | head -n 1 >> "$TMP"
else
	echo "Not readable" >> "$TMP"
fi

# 7. Memory (free -m)
section "Memory (free -m)"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_free" ]; then
	cat "${ROOT}/fake_free" >> "$TMP" 2>&1
elif command -v free >/dev/null 2>&1; then
	free -m >> "$TMP" 2>&1 || true
elif [ -f "${ROOT}/proc/meminfo" ]; then
	cat "${ROOT}/proc/meminfo" >> "$TMP" 2>&1
else
	echo "Memory info not available" >> "$TMP"
fi

# 8. Swap
section "Swap"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_swap" ]; then
	cat "${ROOT}/fake_swap" >> "$TMP" 2>&1
elif command -v swapon >/dev/null 2>&1; then
	swapon --show >> "$TMP" 2>&1 || true
elif [ -f "${ROOT}/proc/swaps" ]; then
	cat "${ROOT}/proc/swaps" >> "$TMP" 2>&1
else
	echo "Swap info not available" >> "$TMP"
fi

# 9. Block Devices (lsblk)
section "Block Devices (lsblk)"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_lsblk" ]; then
	cat "${ROOT}/fake_lsblk" >> "$TMP" 2>&1
elif command -v lsblk >/dev/null 2>&1; then
	lsblk >> "$TMP" 2>&1 || true
elif [ -f "${ROOT}/proc/partitions" ]; then
	cat "${ROOT}/proc/partitions" >> "$TMP" 2>&1
else
	echo "Block device info not available" >> "$TMP"
fi

# 10. Mounts
section "Mounts"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_mounts" ]; then
	cat "${ROOT}/fake_mounts" >> "$TMP" 2>&1
elif [ -f "${ROOT}/proc/mounts" ]; then
	cat "${ROOT}/proc/mounts" >> "$TMP" 2>&1
elif command -v mount >/dev/null 2>&1; then
	mount >> "$TMP" 2>&1 || true
else
	echo "Mount info not available" >> "$TMP"
fi

# 11. /etc/kboot.conf
section "/etc/kboot.conf"
if [ -f "${ROOT}/etc/kboot.conf" ]; then
	cat "${ROOT}/etc/kboot.conf" >> "$TMP" 2>&1
else
	echo "Not found" >> "$TMP"
fi

# 12. /etc/default/ps3-boot
section "/etc/default/ps3-boot"
if [ -f "${ROOT}/etc/default/ps3-boot" ]; then
	cat "${ROOT}/etc/default/ps3-boot" >> "$TMP" 2>&1
else
	echo "Not found" >> "$TMP"
fi

# 13. Boot Target (/run/ps3ssd/init.log)
section "Boot Target (/run/ps3ssd/init.log)"
if [ -f "${ROOT}/run/ps3ssd/init.log" ]; then
	cat "${ROOT}/run/ps3ssd/init.log" >> "$TMP" 2>&1
else
	echo "Not present" >> "$TMP"
fi

# 14. Kernel Messages (dmesg, last 400 lines)
section "Kernel Log (dmesg, last 400 lines)"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_dmesg" ]; then
	tail -n 400 "${ROOT}/fake_dmesg" >> "$TMP" 2>&1
elif command -v dmesg >/dev/null 2>&1; then
	dmesg 2>/dev/null | tail -n 400 >> "$TMP" 2>&1 || echo "dmesg failed or requires elevated privileges" >> "$TMP"
else
	echo "dmesg not available" >> "$TMP"
fi

# 15. Xorg Log (last 200 lines)
section "Xorg Log (last 200 lines)"
XLOG=""
if [ -n "$ROOT" ] && [ -f "${ROOT}/xorg.log" ]; then
	XLOG="${ROOT}/xorg.log"
elif [ -f "${ROOT}${HOME:-}/.local/share/xorg/Xorg.0.log" ]; then
	XLOG="${ROOT}${HOME:-}/.local/share/xorg/Xorg.0.log"
elif [ -n "${SUDO_USER:-}" ] && [ -f "${ROOT}/home/${SUDO_USER}/.local/share/xorg/Xorg.0.log" ]; then
	XLOG="${ROOT}/home/${SUDO_USER}/.local/share/xorg/Xorg.0.log"
else
	for udir in "${ROOT}"/home/*; do
		if [ -f "$udir/.local/share/xorg/Xorg.0.log" ]; then
			XLOG="$udir/.local/share/xorg/Xorg.0.log"
			break
		fi
	done
fi
if [ -z "$XLOG" ] && [ -f "${ROOT}/var/log/Xorg.0.log" ]; then
	XLOG="${ROOT}/var/log/Xorg.0.log"
fi

if [ -n "$XLOG" ] && [ -f "$XLOG" ]; then
	printf 'Source: %s\n' "$XLOG" >> "$TMP"
	tail -n 200 "$XLOG" >> "$TMP" 2>&1
else
	echo "Xorg log not found" >> "$TMP"
fi

# 16. Failed Services (systemctl --failed)
section "Failed Services (systemctl --failed)"
if [ -n "$ROOT" ] && [ -f "${ROOT}/fake_systemctl" ]; then
	cat "${ROOT}/fake_systemctl" >> "$TMP" 2>&1
elif command -v systemctl >/dev/null 2>&1; then
	systemctl --failed --no-pager >> "$TMP" 2>&1 || true
else
	echo "systemctl not available" >> "$TMP"
fi

# 17. Write Guard State (/sys/block/ps3d?/write_guard/*)
section "Write Guard State"
found_guard=0
for dev in "${ROOT}"/sys/block/ps3d*; do
	[ -d "$dev/write_guard" ] || continue
	found_guard=1
	dname="$(basename "$dev")"
	printf '%s:\n' "$dname" >> "$TMP"
	for gf in "$dev/write_guard"/*; do
		[ -f "$gf" ] || continue
		gfname="$(basename "$gf")"
		[ "$gfname" = "ranges" ] && continue # ranges is write-only
		printf '  %s: %s\n' "$gfname" "$(cat "$gf" 2>/dev/null || echo '<unreadable>')" >> "$TMP"
	done
done
if [ "$found_guard" -eq 0 ]; then
	echo "No write guard files found" >> "$TMP"
fi

# Redact private data:
# - IPv4 addresses except 127.0.0.1 -> x.x.x.x
# - MAC addresses -> xx:xx:xx:xx:xx:xx (and xx-xx-xx-xx-xx-xx)
# - User name in paths -> /home/[user] or /[user]/
# - Hostname -> [hostname]
# - Fake root prefix if testing -> stripped

SED_EXPR="
s/127\.0\.0\.1/__LOCAL_LOOPBACK__/g
s/\b[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\b/x.x.x.x/g
s/__LOCAL_LOOPBACK__/127.0.0.1/g
s/\b\([0-9a-fA-F]\{2\}:\)\{5\}[0-9a-fA-F]\{2\}\b/xx:xx:xx:xx:xx:xx/g
s/\b\([0-9a-fA-F]\{2\}-\)\{5\}[0-9a-fA-F]\{2\}\b/xx-xx-xx-xx-xx-xx/g
s#/home/[^/[:space:]\"'\`:;>)]*#/home/[user]#g
"

if [ -n "$CUR_USER" ] && [ "$CUR_USER" != "root" ] && [ ${#CUR_USER} -ge 2 ]; then
	SED_EXPR="${SED_EXPR}
s#/${CUR_USER}/#/[user]/#g
s#/${CUR_USER}\([[:space:]\"'\`:;>)]\|\$\)#/[user]\1#g
s#${CUR_USER}@#[user]@#g
"
fi

if [ -n "$HOST" ] && [ "$HOST" != "localhost" ] && [ "$HOST" != "localhost.localdomain" ] && [ "$HOST" != "(none)" ]; then
	SED_EXPR="${SED_EXPR}
s/\(^\|[[:space:]@\"':=<]\)${HOST}\([[:space:]@\"':;>]\)/\1[hostname]\2/g
s/\(^\|[[:space:]@\"':=<]\)${HOST}\.\([[:space:]]\|\$\)/\1[hostname].\2/g
s/\(^\|[[:space:]@\"':=<]\)${HOST}\$/\1[hostname]/g
"
fi

if [ -n "$ROOT" ]; then
	SED_EXPR="${SED_EXPR}
s#${ROOT}##g
"
fi

sed -e "$SED_EXPR" "$TMP" > "$OUT"
printf 'walnut-bugreport: saved report to %s\n' "$OUT"
exit 0
