129 lines
5.3 KiB
Bash
Executable file
129 lines
5.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# One-time system setup for the CH347 USB-I2C adapter and the RM3100 logger.
|
|
# Idempotent: safe to re-run.
|
|
#
|
|
# Needs sudo for exactly one thing: installing the udev rule that grants this
|
|
# user access to the adapter's USB node.
|
|
|
|
set -euo pipefail
|
|
|
|
VID=1a86
|
|
PID=55db
|
|
RULE_FILE=/etc/udev/rules.d/99-ch347.rules
|
|
|
|
cd "$(dirname "$0")"
|
|
|
|
echo "== 1. Checking for the CH347 adapter =="
|
|
if ! lsusb -d "$VID:$PID" >/dev/null 2>&1; then
|
|
echo "ERROR: no device $VID:$PID found on the USB bus." >&2
|
|
echo " Plug in the Waveshare adapter and make sure it is in Mode 1" >&2
|
|
echo " (UART1+I2C+SPI): DTR1 pulled high, RTS1 pulled low." >&2
|
|
exit 1
|
|
fi
|
|
lsusb -d "$VID:$PID"
|
|
|
|
echo
|
|
echo "== 2. Installing udev rule =="
|
|
# Access is granted two ways, because no single mechanism covers every distro:
|
|
#
|
|
# TAG+="uaccess" systemd-logind puts an ACL on the node for whoever is
|
|
# logged in at the local seat. Needs no group and no logout,
|
|
# and is the only thing that works unaided on atomic Fedora
|
|
# (Bazzite, Silverblue, Kinoite) -- see section 4.
|
|
# GROUP=/MODE= the traditional fallback, for ssh sessions and seatless
|
|
# systems where there is no local seat for uaccess to grant.
|
|
#
|
|
# The group is picked from those that exist. plugdev is a Debian convention and
|
|
# is absent on Fedora; naming a group that does not exist makes udev log an
|
|
# error and leave the node owned by root, which is worse than omitting it.
|
|
ACCESS_GROUP=""
|
|
for g in plugdev dialout; do
|
|
if getent group "$g" >/dev/null; then ACCESS_GROUP=$g; break; fi
|
|
done
|
|
|
|
RULE="SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"$VID\", ATTRS{idProduct}==\"$PID\", TAG+=\"uaccess\""
|
|
if [[ -n "$ACCESS_GROUP" ]]; then
|
|
RULE="$RULE, GROUP=\"$ACCESS_GROUP\", MODE=\"0660\""
|
|
echo "Access via uaccess, falling back to group '$ACCESS_GROUP'."
|
|
else
|
|
echo "No plugdev or dialout group here; relying on uaccess alone."
|
|
fi
|
|
|
|
if [[ -f "$RULE_FILE" ]] && [[ "$(cat "$RULE_FILE")" == "$RULE" ]]; then
|
|
echo "$RULE_FILE already up to date, skipping."
|
|
else
|
|
echo "Writing $RULE_FILE (needs sudo)..."
|
|
printf '%s\n' "$RULE" | sudo tee "$RULE_FILE" >/dev/null
|
|
sudo udevadm control --reload-rules
|
|
# --action=add, not the default 'change': the uaccess builtin that sets the
|
|
# ACL only runs on add, so a change event would install the rule without
|
|
# granting anything until the next replug. Matched to this device alone so
|
|
# re-running setup does not re-add every USB device on the system.
|
|
sudo udevadm trigger --action=add --subsystem-match=usb \
|
|
--attr-match=idVendor="$VID" --attr-match=idProduct="$PID"
|
|
# trigger returns before the rule has necessarily been applied.
|
|
sudo udevadm settle
|
|
echo "Installed."
|
|
fi
|
|
|
|
echo
|
|
echo "== 3. Creating virtualenv and installing pyusb =="
|
|
if [[ ! -d .venv ]]; then
|
|
python3 -m venv .venv
|
|
echo "Created .venv"
|
|
else
|
|
echo ".venv already exists"
|
|
fi
|
|
./.venv/bin/pip install --quiet --upgrade pip
|
|
./.venv/bin/pip install --quiet pyusb
|
|
echo "pyusb $(./.venv/bin/python -c 'import usb; print(usb.__version__)') installed"
|
|
|
|
echo
|
|
echo "== 4. Verifying device node permissions =="
|
|
# The node itself is the ground truth, so nothing above is warned about
|
|
# speculatively -- group membership only matters if uaccess did not already
|
|
# cover it, and that is visible here.
|
|
NODE=$(lsusb -d "$VID:$PID" | head -1 |
|
|
sed -E 's|Bus ([0-9]+) Device ([0-9]+).*|/dev/bus/usb/\1/\2|')
|
|
ls -l "$NODE"
|
|
getfacl -p "$NODE" 2>/dev/null | grep -E "^user:[^:]+:" || true
|
|
|
|
if [[ -w "$NODE" ]]; then
|
|
echo "OK: $NODE is writable by $(id -un)."
|
|
else
|
|
USER_NAME=$(id -un)
|
|
echo "WARNING: $NODE is not writable by $USER_NAME." >&2
|
|
echo >&2
|
|
echo " uaccess grants nothing over ssh or without a local seat, so fall" >&2
|
|
echo " back to the group. Try, in order:" >&2
|
|
echo >&2
|
|
echo " 1. Unplug and replug the adapter, so the rule applies to a fresh" >&2
|
|
echo " node. This is enough on a local desktop session." >&2
|
|
if [[ -n "$ACCESS_GROUP" ]] &&
|
|
! id -nG | tr ' ' '\n' | grep -qx "$ACCESS_GROUP"; then
|
|
echo >&2
|
|
if [[ -f /run/ostree-booted ]]; then
|
|
# usermod edits /etc/group directly, while getent also sees
|
|
# /usr/lib/group through nss_altfiles. On rpm-ostree systems the
|
|
# group is often only in the latter, so it looks present to every
|
|
# query and still fails to add: "group '$ACCESS_GROUP' does not
|
|
# exist". Copying the line across is what makes usermod agree.
|
|
echo " 2. This is an rpm-ostree system (Bazzite/Silverblue), where" >&2
|
|
echo " '$ACCESS_GROUP' may exist only in /usr/lib/group. usermod" >&2
|
|
echo " reads /etc/group alone and will refuse. Copy it over:" >&2
|
|
echo " grep -E '^$ACCESS_GROUP:' /usr/lib/group | sudo tee -a /etc/group" >&2
|
|
echo " then:" >&2
|
|
else
|
|
echo " 2. Add yourself to the group:" >&2
|
|
fi
|
|
echo " sudo usermod -aG $ACCESS_GROUP $USER_NAME" >&2
|
|
echo " and log out and back in for it to take effect." >&2
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo "Setup complete. Run the logger with:"
|
|
echo " ./.venv/bin/python logger.py --duration 10"
|
|
echo "or, to check wiring only:"
|
|
echo " ./.venv/bin/python logger.py --scan-only"
|