mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:07:04 +01:00
Vendored makepkg's run_pacman() waits on /var/lib/pacman/db.lck existence
with no timeout BEFORE invoking pacman, so an orphaned lock hangs
`makepkg -i` forever ("Pacman is currently in use, please wait...") and
the pacman wrapper's stale-lock cleanup never gets a turn. Wrap makepkg
(mirroring the pacman-wrapper pattern) to clear an orphaned lock up front.
- Extract stale-lock logic into shared pacman_lock_lib.sh; pacman_wrapper
sources it after integrity verification and it is in the SHA256 manifest
- makepkg_wrapper: pre-flight clear for -i/--install; bypass on FAKEROOTKEY
and non-install; FAIL OPEN if the lib is missing (never brick makepkg)
- Cross-user pacman_process_running() pgrep guard: unprivileged makepkg can
detect a root pacman its own fuser cannot see (avoids removing a live lock)
- Upgrade survival: rewrap hook (Install/Upgrade only) + check_makepkg_wrapper
drift verifier + both wrappers wired into the hourly periodic driver
- Fix reorg path bugs: stale ~/linux-configuration periodic path,
$CONFIG_DIR/hosts -> scripts/periodic_background/hosts, test WRAPPER_DIR;
refresh .orig only when target isn't already our symlink (no exec loop)
- Tests: test_makepkg_wrapper.sh (18) incl. hook Operation validity; add
lib-integrity coverage to test_pacman_wrapper_security.sh (also path-fixed)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
121 lines
5.4 KiB
Bash
Executable File
121 lines
5.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# Tests for the makepkg wrapper + shared stale-lock library.
|
|
#
|
|
# Covers: script syntax, structural invariants (fail-open source guard,
|
|
# FAKEROOTKEY bypass, .orig-refresh, integrity), and the actual stale-lock
|
|
# behaviour exercised against a TEMP lock file (never the real system lock).
|
|
|
|
set -u
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PAC_DIR="$SCRIPT_DIR/../scripts/periodic_background/digital_wellbeing/pacman"
|
|
LIB="$PAC_DIR/pacman_lock_lib.sh"
|
|
WRAPPER="$PAC_DIR/makepkg_wrapper.sh"
|
|
INSTALLER="$PAC_DIR/install_makepkg_wrapper.sh"
|
|
REWRAP="$PAC_DIR/rewrap_pkg_managers.sh"
|
|
HOOK="$PAC_DIR/96-restore-pkg-wrappers.hook"
|
|
|
|
pass=0
|
|
fail=0
|
|
ok() { echo "✓ $1"; pass=$((pass + 1)); }
|
|
bad() { echo "✗ $1"; fail=$((fail + 1)); }
|
|
|
|
echo "=== Testing makepkg wrapper + shared stale-lock library ==="
|
|
|
|
# --- Syntax ---------------------------------------------------------------
|
|
for f in "$LIB" "$WRAPPER" "$INSTALLER" "$REWRAP"; do
|
|
if bash -n "$f"; then ok "syntax valid: $(basename "$f")"; else bad "syntax error: $(basename "$f")"; fi
|
|
done
|
|
|
|
# --- Structural invariants ------------------------------------------------
|
|
if grep -q 'PACMAN_LOCK_FILE:-/var/lib/pacman/db.lck' "$LIB"; then
|
|
ok "lib lock path is overridable for tests (defaults to real path)"
|
|
else bad "lib does not parameterise PACMAN_LOCK_FILE"; fi
|
|
|
|
if grep -q 'pacman_process_running' "$LIB"; then
|
|
ok "lib has cross-user pgrep guard (pacman_process_running)"
|
|
else bad "lib missing cross-user pgrep guard"; fi
|
|
|
|
if grep -q 'FAKEROOTKEY' "$WRAPPER"; then
|
|
ok "wrapper has FAKEROOTKEY bypass fast-path"
|
|
else bad "wrapper missing FAKEROOTKEY bypass"; fi
|
|
|
|
# Fail-open: a missing lib must exec real makepkg rather than break it.
|
|
if awk '/-r "\$_LIB_DIR\/pacman_lock_lib.sh"/{g=1} g&&/exec "\$MAKEPKG_BIN"/{seen=1} END{exit !seen}' "$WRAPPER"; then
|
|
ok "wrapper fails OPEN when lib missing (execs real makepkg)"
|
|
else bad "wrapper does not fail open on missing lib"; fi
|
|
|
|
if grep -q 'if \[ ! -L /usr/bin/makepkg \]' "$INSTALLER"; then
|
|
ok "installer refreshes makepkg.orig only when not our symlink (no exec loop)"
|
|
else bad "installer .orig guard missing/incorrect"; fi
|
|
|
|
if grep -q 'When = PostTransaction' "$HOOK" && grep -q 'rewrap_pkg_managers.sh' "$HOOK"; then
|
|
ok "survival hook is PostTransaction and runs rewrap helper"
|
|
else bad "survival hook malformed"; fi
|
|
|
|
# Invalid alpm Operation values abort EVERY pacman transaction (alpm parses all
|
|
# hooks up front), so the hook must use only Install/Upgrade/Remove.
|
|
if grep -E '^Operation' "$HOOK" | grep -qvE '^Operation = (Install|Upgrade|Remove)$'; then
|
|
bad "hook has an invalid alpm Operation value (would break all pacman transactions)"
|
|
else ok "hook uses only valid alpm Operation values"; fi
|
|
|
|
# --- Functional (temp lock, no root) --------------------------------------
|
|
TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$TMP"; jobs -p | xargs -r kill 2>/dev/null || true' EXIT
|
|
LOCK="$TMP/db.lck"
|
|
STUB="$TMP/makepkg.stub"
|
|
printf '#!/bin/bash\nprintf "%%s" "$*" > "%s/args"\n' "$TMP" > "$STUB"
|
|
chmod +x "$STUB"
|
|
|
|
# Source the lib once. check_and_handle_db_lock reads $PACMAN_LOCK_FILE at call
|
|
# time, so pointing it at the temp lock before sourcing is enough for all cases.
|
|
export PACMAN_LOCK_FILE="$LOCK"
|
|
# shellcheck source=../scripts/periodic_background/digital_wellbeing/pacman/pacman_lock_lib.sh
|
|
source "$LIB"
|
|
|
|
# orphaned + --noconfirm -> cleared
|
|
: > "$LOCK"
|
|
check_and_handle_db_lock --noconfirm >/dev/null 2>&1 || true
|
|
if [[ ! -e $LOCK ]]; then ok "orphaned lock + --noconfirm cleared"; else bad "orphaned lock not cleared"; fi
|
|
|
|
# absent -> no-op, returns 0
|
|
if check_and_handle_db_lock --noconfirm >/dev/null 2>&1; then ok "absent lock is a no-op"; else bad "absent lock returned non-zero"; fi
|
|
|
|
# old (>=600s) -> auto-removed without --noconfirm
|
|
: > "$LOCK"
|
|
touch -d '2 hours ago' "$LOCK"
|
|
check_and_handle_db_lock >/dev/null 2>&1 || true
|
|
if [[ ! -e $LOCK ]]; then ok "old orphaned lock auto-removed without --noconfirm"; else bad "age-based removal failed"; fi
|
|
|
|
# running 'pacman' process -> removal blocked (cross-user pgrep guard)
|
|
: > "$LOCK"
|
|
FAKE="$TMP/pacman"
|
|
cp /bin/sleep "$FAKE"
|
|
setsid "$FAKE" 20 &
|
|
fp=$!
|
|
sleep 0.3
|
|
if check_and_handle_db_lock --noconfirm >/dev/null 2>&1; then blocked=0; else blocked=1; fi
|
|
if [[ $blocked -eq 1 && -e $LOCK ]]; then ok "running 'pacman' process blocks removal"; else bad "pgrep guard failed"; fi
|
|
kill "$fp" 2>/dev/null || true
|
|
sleep 0.2
|
|
rm -f "$LOCK"
|
|
|
|
# wrapper: non-install invocation bypasses, leaves lock, execs real makepkg
|
|
: > "$LOCK"
|
|
MAKEPKG_BIN="$STUB" bash "$WRAPPER" --version >/dev/null 2>&1 || true
|
|
if [[ -e $LOCK && "$(cat "$TMP/args" 2>/dev/null)" == "--version" ]]; then ok "wrapper: non-install bypasses, lock untouched"; else bad "wrapper non-install bypass failed"; fi
|
|
|
|
# wrapper: -sif clears orphaned lock then execs real makepkg
|
|
: > "$LOCK"
|
|
MAKEPKG_BIN="$STUB" bash "$WRAPPER" -sif --noconfirm >/dev/null 2>&1 || true
|
|
if [[ ! -e $LOCK && "$(cat "$TMP/args" 2>/dev/null)" == "-sif --noconfirm" ]]; then ok "wrapper: -sif clears orphaned lock then execs makepkg"; else bad "wrapper install pre-flight failed"; fi
|
|
|
|
# wrapper: FAKEROOTKEY (build sandbox) bypasses, keeps lock
|
|
: > "$LOCK"
|
|
MAKEPKG_BIN="$STUB" FAKEROOTKEY=deadbeef bash "$WRAPPER" -sif --noconfirm >/dev/null 2>&1 || true
|
|
if [[ -e $LOCK ]]; then ok "wrapper: FAKEROOTKEY bypass keeps lock"; else bad "wrapper fakeroot bypass failed"; fi
|
|
|
|
echo ""
|
|
echo "=== Result: $pass passed, $fail failed ==="
|
|
if [[ $fail -eq 0 ]]; then echo "=== All Tests Passed! ==="; else exit 1; fi
|