labfy-rgb/packaging/apply-udev-rule.sh
2026-11-09 19:01:00 +01:00

65 lines
2.8 KiB
Bash

#!/usr/bin/env bash
# WHY: a global hidraw trigger would affect unrelated devices. Discover exactly
# one I2C/ASUS/5570 controller and trigger only its current hidraw sysfs node.
# CONTRACT: default/--check is read-only. --apply requires root AND the owner's
# prior approval; no sudo, chmod of /dev, module change or HID report is issued.
set -euo pipefail
mode=${1:---check}
if [[ $# -gt 1 || ( $mode != --check && $mode != --apply ) ]]; then
printf 'Usage: %s [--check|--apply]\n' "$0" >&2
exit 2
fi
script_dir=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
rule=$script_dir/70-labfy-rgb.rules
target=/etc/udev/rules.d/70-labfy-rgb.rules
udevadm verify "$rule"
# Refuse a broadened proposal, even if its udev syntax is valid.
expected='SUBSYSTEM=="hidraw", KERNEL=="hidraw*", SUBSYSTEMS=="hid", KERNELS=="0018:0B05:5570.*", TAG+="uaccess"'
mapfile -t active_lines < <(sed '/^[[:space:]]*#/d; /^[[:space:]]*$/d' "$rule")
if [[ ${#active_lines[@]} -ne 1 || ${active_lines[0]} != "$expected" ]]; then
printf 'Rule differs from the reviewed narrow match; refusing.\n' >&2
exit 5
fi
shopt -s nullglob
matches=()
for candidate in /sys/class/hidraw/hidraw*; do
identity=''
while IFS= read -r line; do
if [[ $line == HID_ID=* ]]; then identity=${line#HID_ID=}; fi
done < "$candidate/device/uevent"
[[ $identity == 0018:00000B05:00005570 ]] || continue
parent=$(readlink -f -- "$candidate/device")
[[ ${parent##*/} == 0018:0B05:5570.* ]] || continue
matches+=("$candidate")
done
if [[ ${#matches[@]} -ne 1 ]]; then
printf 'Expected exactly one matching controller; found %s. No changes.\n' "${#matches[@]}" >&2
exit 3
fi
sysfs=${matches[0]}
node=/dev/${sysfs##*/}
if [[ -L $target ]]; then
printf 'Refusing existing symlink: %s\n' "$target" >&2
exit 5
fi
if [[ -e $target ]] && ! cmp -s -- "$rule" "$target"; then
printf 'Refusing to overwrite a different existing rule: %s\n' "$target" >&2
exit 5
fi
printf 'Rule: %s\nTarget: %s\nDevice: %s\nSysfs: %s\n' "$rule" "$target" "$node" "$sysfs"
if [[ $mode == --check ]]; then
printf 'Read-only plan: install rule root:root 0644, reload rules, trigger change on this node only, settle, inspect ACL.\nNo changes applied.\n'
exit 0
fi
if (( EUID != 0 )); then
printf -- '--apply requires administrator execution and prior explicit approval. No changes.\n' >&2
exit 4
fi
# No overwrite of an unrelated file. A reload reparses all rules, while the
# event is limited to the one discovered node; neither operation sends HID data.
if [[ ! -e $target ]]; then install -o root -g root -m 0644 -- "$rule" "$target"; fi
udevadm control --reload-rules
udevadm trigger --action=change "$sysfs"
udevadm settle --timeout=10
getfacl -p -- "$node"
printf 'Rule application completed. Run labfy-rgb status as your normal user to verify access.\n'