#!/usr/bin/env bash # Tell somebody when a program crashes. # # On GNOME, ABRT says so. Under a hand-assembled Hyprland desktop nothing does, # and applications die silently -- which is most of how "Linux is flaky" gets # earned. Fedora ships systemd-coredump by default, so the information is # already there; nobody is reading it. # # Follows the journal for systemd-coredump's own message id and reports each # program once per session. # # ONCE PER SESSION IS THE WHOLE DESIGN. This machine's portal backend crashes # between eleven and sixty times a day -- see the portal-stability check in # panama-doctor -- and a notification per crash would be a notification every # few minutes for something the user can do nothing about. The first one is # news; the fortieth is why people turn notifications off. The health page # carries the running count for anyone who wants it. set -uo pipefail PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}" # systemd-coredump's MESSAGE_ID. Matching on this rather than on text keeps # working when the wording changes and never matches a program that merely # mentions the word "crash" in its own logs. readonly COREDUMP_MESSAGE_ID='fc2e22bc6ee647b6b90729ab34a250b1' command -v journalctl >/dev/null 2>&1 || exit 0 command -v notify-send >/dev/null 2>&1 || exit 0 # The shell owns org.freedesktop.Notifications, and the crash most worth # reporting is the one that took the shell with it. Waiting means that report # arrives rather than vanishing into a bus nobody is serving. for _ in $(seq 1 60); do busctl --user status org.freedesktop.Notifications >/dev/null 2>&1 && break sleep 1 done declare -A reported=() # -f from now, not from the boot: a session that starts after a crash should # not open with a notification about something the user has already lived # through and cannot act on. journalctl --user -f -n 0 --output=json MESSAGE_ID="$COREDUMP_MESSAGE_ID" 2>/dev/null \ | while IFS= read -r line; do [[ -n "$line" ]] || continue uid="$(jq -r '.COREDUMP_UID // empty' <<<"$line" 2>/dev/null)" exe="$(jq -r '.COREDUMP_EXE // empty' <<<"$line" 2>/dev/null)" comm="$(jq -r '.COREDUMP_COMM // empty' <<<"$line" 2>/dev/null)" # Another user's crash is not this session's business, and reporting it # would leak what they are running. [[ "$uid" == "$(id -u)" ]] || continue [[ -n "$exe" || -n "$comm" ]] || continue # The executable name first: COREDUMP_COMM is the kernel's comm field # and is truncated to fifteen characters, so it reports # "panama-test-cra" for a program called panama-test-crasher. if [[ -n "$exe" ]]; then program="$(basename "$exe")" else program="$comm" fi [[ -z "${reported[$program]:-}" ]] || continue reported[$program]=1 notify-send --icon=dialog-error-symbolic --app-name=Panama \ "$program stopped unexpectedly" \ "It crashed and was not able to recover. System Health has the details." \ 2>/dev/null || true done