testsAndMisc/linux_configuration/scripts/single_use/fixes/lib/bt_pairing.sh
Krzysztof kuhy Rudnicki 0e9cc0ce95
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Test bt_pairing.sh to 100%: allowlist 97 -> 96
Second of the 16 untested fixes/lib libs. 65/65 = 100.00% through
run_all.sh, twice, the second at --min 100 (exit 0), then cleared from the
allowlist and confirmed by the real gate with no exemption.

Two findings worth keeping:

- udevadm was NOT in the harness's stub list, so `udevadm control
  --reload-rules` was reaching the REAL binary during the suite. A
  prepended stub dir cannot hide a tool the host actually has. Caught by
  asserting on the recorded call rather than by reading the code, which is
  the argument for asserting on calls at all.

- _disable_usb_autosuspend writes /etc/udev/rules.d/50-bluetooth-no-
  autosuspend.rules. Binding /etc in the jail would have covered it, and
  was rejected: run_all.sh runs UN-jailed in ci_mirror.sh and in the
  shell-tests workflow, as its own header says, so a bind mount protects
  the coverage run and leaves every bare run writing to a real root-owned
  file. Instead the lib gained a UDEV_RULES_DIR override, defaulting to the
  real path -- the same env-override seam pacman_hook_stall_harness.sh uses
  for PACMAN_LOG/CACHE_DIR/PACMAN_LOCK.

Proof the suite is safe where it runs un-jailed: after a bare run_all.sh,
the real rule file still has its 2026-02-21 mtime (it is a genuine artifact
of a production run six months ago) and the probed power/control values are
unchanged. The jail cannot give that check.

fix_usb_autosuspend globs the real /sys/bus/usb/devices/*/ with no override,
so its branches are driven through stubbed lsusb output: ffff:ffff matches
nothing on any machine and reaches the not-found arm, while 046d:c077
(control=on) and 046d:0825 (control=auto) are read-only probes, each guarded
so the case self-skips with a note if the host state changes rather than
silently asserting nothing.

Split across two test files to stay under the 250-line cap; run_all.sh globs
test_*.sh, so splitting costs nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 16:47:40 +02:00

132 lines
4.2 KiB
Bash
Executable File

#!/bin/bash
# Stale pairing removal, service restart and USB autosuspend.
#
# Sourced by fix_bluetooth.sh; split out to keep it under the 250-line
# cap. Sourced rather than run, so it inherits the caller's strict mode
# and the variables defined above the source line.
# ==========================================================================
# 5. Remove stale pairing for target device (if specified)
# ==========================================================================
remove_stale_pairing() {
if [[ -z $TARGET_MAC ]]; then
return 0
fi
echo ""
log_info "Checking for stale pairing with $TARGET_MAC..."
local info
info=$(_btctl info "$TARGET_MAC" || true)
if echo "$info" | grep -q "Device $TARGET_MAC"; then
local paired
paired=$(echo "$info" | grep "Paired:" | awk '{print $2}')
local connected
connected=$(echo "$info" | grep "Connected:" | awk '{print $2}')
if [[ $paired == "yes" && $connected == "no" ]]; then
log_warn "Device is paired but NOT connected — may have stale pairing."
apply_fix "Removing stale pairing for $TARGET_MAC" \
_btctl remove "$TARGET_MAC"
elif [[ $paired == "no" ]]; then
log_info "Device is not currently paired."
else
log_ok "Device is paired and connected."
fi
else
log_info "Device $TARGET_MAC not found in bluetoothctl. Fresh pairing needed."
fi
}
# ==========================================================================
# 6. Restart Bluetooth service to apply changes
# ==========================================================================
restart_bluetooth() {
echo ""
if [[ $FIXES_APPLIED -gt 0 ]]; then
apply_fix "Restarting bluetooth.service to apply changes" \
systemctl restart bluetooth.service
else
log_info "No fixes applied — skipping service restart."
fi
}
# ==========================================================================
# 7. Disable USB autosuspend for Bluetooth adapter
# ==========================================================================
fix_usb_autosuspend() {
echo ""
log_info "Checking USB autosuspend for Bluetooth adapter..."
local bt_usb
bt_usb=$(lsusb 2>/dev/null | grep -i bluetooth | head -1 || true)
if [[ -z $bt_usb ]]; then
return 0
fi
local usb_id
usb_id=$(echo "$bt_usb" | grep -oP 'ID \K[0-9a-f]{4}:[0-9a-f]{4}' || true)
if [[ -z $usb_id ]]; then
return 0
fi
local vendor="${usb_id%%:*}"
local product="${usb_id##*:}"
# Find the sysfs device path
local sysfs_path=""
local dev_path
for dev_path in /sys/bus/usb/devices/*/; do
if [[ -f "${dev_path}idVendor" && -f "${dev_path}idProduct" ]]; then
local v p
v=$(cat "${dev_path}idVendor" 2>/dev/null || true)
p=$(cat "${dev_path}idProduct" 2>/dev/null || true)
if [[ $v == "$vendor" && $p == "$product" ]]; then
sysfs_path="$dev_path"
break
fi
fi
done
if [[ -z $sysfs_path ]]; then
log_warn "Could not find sysfs path for BT adapter."
return 0
fi
local power_control="${sysfs_path}power/control"
if [[ -f $power_control ]]; then
local current
current=$(cat "$power_control" 2>/dev/null || true)
if [[ $current != "on" ]]; then
log_warn "USB autosuspend is enabled ($current) — can cause audio dropouts."
apply_fix "Disabling USB autosuspend for BT adapter" \
_disable_usb_autosuspend "$power_control" "$vendor" "$product"
else
log_ok "USB autosuspend already disabled."
fi
fi
}
_disable_usb_autosuspend() {
local power_control="$1"
local vendor="$2"
local product="$3"
# Immediate fix
echo "on" >"$power_control"
# Persistent udev rule. UDEV_RULES_DIR defaults to the real directory and
# is only overridden by the test suite, which cannot be allowed to write
# to /etc: lib/tests/run_all.sh runs UN-jailed in ci_mirror.sh and in CI,
# so a bind mount would protect the coverage run and nothing else.
local rule_file="${UDEV_RULES_DIR:-/etc/udev/rules.d}/50-bluetooth-no-autosuspend.rules"
local rule="ACTION==\"add\", SUBSYSTEM==\"usb\", ATTR{idVendor}==\"$vendor\", ATTR{idProduct}==\"$product\", ATTR{power/control}=\"on\""
if [[ ! -f $rule_file ]] || ! grep -qF "$vendor" "$rule_file" 2>/dev/null; then
echo "$rule" >"$rule_file"
udevadm control --reload-rules 2>/dev/null || true
log_info "Created persistent udev rule: $rule_file"
fi
}