One door per name on the IPC bus
A second IpcHandler with an already-used target does not error -- it silently shadows the first, and for the polkit target that means the agent's authentication requests stop reaching the prompt: every password dialog on the desktop, gone without a message. That duplicate nearly shipped once, because the handlers live scattered through a long shell.qml. The contract pairs every IpcHandler with its quoted target across the shell's QML (harnesses excluded -- each is its own root), fails on any name declared twice, and refuses to pass on an empty scan so a declaration-format change cannot quietly blind it.
This commit is contained in:
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# One IpcHandler per target name.
|
||||
#
|
||||
# `qs ipc call <target> ...` resolves by name, and the handlers are spread
|
||||
# across a shell.qml several hundred lines long -- a second IpcHandler with an
|
||||
# already-used target is exactly the mistake a reader makes there, and it does
|
||||
# not error: one handler silently shadows or conflicts with the other. For the
|
||||
# polkit target that is not cosmetic -- the agent delivers authentication
|
||||
# requests through it, so a shadowed `begin` breaks every password prompt on
|
||||
# the desktop. This nearly shipped once; now it cannot.
|
||||
#
|
||||
# Harness files are excluded: each is its own root, never loaded beside the
|
||||
# shell, so sharing a target with it is not a conflict.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
shell_dir="$repo_dir/config/dot/quickshell"
|
||||
|
||||
findings=()
|
||||
note() { findings+=("$1"); }
|
||||
|
||||
# target name <TAB> file, one line per IpcHandler declaration. The awk pairs
|
||||
# each `IpcHandler {` with the next quoted `target:` line in the same file,
|
||||
# which is how every handler in this codebase is written.
|
||||
declarations="$(find "$shell_dir" -name '*.qml' -not -name '*-harness.qml' -exec awk '
|
||||
/IpcHandler *{/ { pending = 1 }
|
||||
pending && /target: "/ {
|
||||
match($0, /target: "[^"]+"/)
|
||||
printf "%s\t%s\n", substr($0, RSTART + 9, RLENGTH - 10), FILENAME
|
||||
pending = 0
|
||||
}
|
||||
' {} +)"
|
||||
|
||||
# The scan finding almost nothing means the declaration format changed, not
|
||||
# that the handlers went away -- fail loudly rather than pass on an empty read.
|
||||
total="$(grep -c . <<<"$declarations" || true)"
|
||||
(( total >= 5 )) \
|
||||
|| note "only $total IpcHandler targets were found; the scan no longer matches how handlers are declared"
|
||||
|
||||
while read -r count target; do
|
||||
[[ -n "$target" ]] || continue
|
||||
if (( count > 1 )); then
|
||||
files="$(awk -F'\t' -v t="$target" '$1 == t { print $2 }' <<<"$declarations" \
|
||||
| sed "s|$repo_dir/||" | paste -sd ', ')"
|
||||
note "the \"$target\" IPC target is declared $count times ($files); one silently shadows the other"
|
||||
fi
|
||||
done < <(cut -f1 <<<"$declarations" | sort | uniq -c)
|
||||
|
||||
if (( ${#findings[@]} > 0 )); then
|
||||
printf 'ipc targets contract: %d finding(s)\n' "${#findings[@]}" >&2
|
||||
printf ' - %s\n' "${findings[@]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'ipc targets contract: PASS (%d targets, each declared once)\n' "$total"
|
||||
Reference in New Issue
Block a user