Make network-device detection work without lspci/lsusb installed
Deploy / deploy (push) Successful in 2s
Deploy / deploy (push) Successful in 2s
pciutils/usbutils aren't guaranteed present, especially on minimal or live-boot systems — exactly the kind of environment someone testing Archipelago compatibility is likely to be running. Rewrote WiFi/ethernet detection to read /sys/class/net/$iface/device directly first (vendor, device, driver via uevent — always available, no package needed), the same approach as manually finding a chip by hand: enumerate interfaces, read vendor/device IDs from sysfs, get the driver from uevent/modalias. Only reaches for lspci/lsusb, when installed, to turn a hex ID into a friendly name; falls back to a small built-in vendor-ID table (Intel, Broadcom, Realtek, Atheros, MediaTek, Ralink) plus the raw ID + driver name otherwise. Also now handles USB and SDIO wifi chips, not just PCI. Found and fixed a real bug while testing this against actual hardware (this box's virtio NIC): the naive `basename` of the resolved device path breaks for drivers like virtio-net that wrap the real PCI function one level deeper in sysfs (.../0000:00:12.0/virtio2) — basename alone grabs "virtio2", which lspci -s rejects as "Invalid slot number". Fixed by extracting the last domain:bus:device.function-shaped path component instead of trusting the final one. Verified for real, not just written: ran with lspci installed (gets the friendly "Red Hat, Inc. Virtio network device" name), then genuinely removed lspci/lsusb from the system (moved the binaries aside, restored after) and reran — falls back correctly to vendor ID + driver name instead of erroring or going blank. Passes shellcheck clean both times. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+115
-15
@@ -91,7 +91,109 @@ if have lsblk && have findmnt; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- WiFi chip: try PCI, then USB, then fall back to the driver name ---
|
# --- network device naming: sysfs first, lspci/lsusb layered on top ---
|
||||||
|
# lspci/lsusb aren't guaranteed to be installed (pciutils/usbutils are
|
||||||
|
# separate packages, often missing on minimal/live-boot systems) — but
|
||||||
|
# /sys is always there. Same approach as finding a chip by hand: read
|
||||||
|
# vendor/device IDs straight from sysfs, and use lspci/lsusb only when
|
||||||
|
# present to turn a hex ID like 0x8086 into "Intel" instead of guessing.
|
||||||
|
|
||||||
|
pci_vendor_name() {
|
||||||
|
case "$1" in
|
||||||
|
0x8086) echo "Intel" ;;
|
||||||
|
0x14e4) echo "Broadcom" ;;
|
||||||
|
0x10ec) echo "Realtek" ;;
|
||||||
|
0x168c|0x1969) echo "Qualcomm Atheros" ;;
|
||||||
|
0x02df|0x0e8d) echo "MediaTek" ;;
|
||||||
|
0x1814) echo "Ralink" ;;
|
||||||
|
*) echo "" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_vendor_name() {
|
||||||
|
case "$1" in
|
||||||
|
8086) echo "Intel" ;;
|
||||||
|
0a5c) echo "Broadcom" ;;
|
||||||
|
0bda) echo "Realtek" ;;
|
||||||
|
0cf3) echo "Qualcomm Atheros" ;;
|
||||||
|
148f) echo "Ralink/MediaTek" ;;
|
||||||
|
*) echo "" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# describe_net_device IFACE — best-effort human description of the hardware
|
||||||
|
# backing a network interface. Walks /sys/class/net/$iface/device to find
|
||||||
|
# whether it's a PCI, USB, or SDIO device, reads the IDs directly, and only
|
||||||
|
# reaches for lspci/lsusb (if installed) to prettify the name.
|
||||||
|
describe_net_device() {
|
||||||
|
local iface="$1" devpath realpath driver="" name=""
|
||||||
|
devpath="/sys/class/net/$iface/device"
|
||||||
|
[ -e "$devpath" ] || return 0
|
||||||
|
realpath="$(readlink -f "$devpath")"
|
||||||
|
[ -r "$devpath/uevent" ] && driver="$(awk -F= '/^DRIVER=/ {print $2}' "$devpath/uevent")"
|
||||||
|
|
||||||
|
case "$realpath" in
|
||||||
|
*/pci*)
|
||||||
|
local vendor device pciid vname
|
||||||
|
vendor="$(cat "$devpath/vendor" 2>/dev/null)"
|
||||||
|
device="$(cat "$devpath/device" 2>/dev/null)"
|
||||||
|
# Not just basename: some drivers (virtio-net, and others that wrap a
|
||||||
|
# PCI function in a virtual sub-bus) resolve one level deeper than
|
||||||
|
# the actual PCI address, e.g. .../0000:00:12.0/virtio2 — the real
|
||||||
|
# slot is the LAST domain:bus:device.function-shaped path component,
|
||||||
|
# not necessarily the final one. Confirmed by hand: basename alone
|
||||||
|
# gave "virtio2", which lspci -s rejects as "Invalid slot number".
|
||||||
|
pciid="$(echo "$realpath" | grep -oE '[0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-9a-f]' | tail -1)"
|
||||||
|
if have lspci && [ -n "$pciid" ]; then
|
||||||
|
name="$(lspci -s "$pciid" 2>/dev/null | sed -E 's/^[0-9a-f:.]+ [^:]+: //')"
|
||||||
|
fi
|
||||||
|
if [ -z "$name" ] && [ -n "$vendor" ]; then
|
||||||
|
vname="$(pci_vendor_name "$vendor")"
|
||||||
|
name="${vname:-PCI vendor $vendor} device $device"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*/usb*)
|
||||||
|
local usbdir idv idp vname
|
||||||
|
usbdir="$realpath"
|
||||||
|
# The net device's "device" symlink usually points at the USB
|
||||||
|
# *interface*, not the device itself — walk up until we find the
|
||||||
|
# level that actually has idVendor/idProduct.
|
||||||
|
while [ -n "$usbdir" ] && [ "$usbdir" != "/" ] && [ ! -r "$usbdir/idVendor" ]; do
|
||||||
|
usbdir="$(dirname "$usbdir")"
|
||||||
|
done
|
||||||
|
if [ -r "$usbdir/idVendor" ]; then
|
||||||
|
idv="$(cat "$usbdir/idVendor" 2>/dev/null)"
|
||||||
|
idp="$(cat "$usbdir/idProduct" 2>/dev/null)"
|
||||||
|
if have lsusb; then
|
||||||
|
name="$(lsusb -d "${idv}:${idp}" 2>/dev/null | head -1 | sed -E 's/^Bus [0-9]+ Device [0-9]+: ID [0-9a-f]{4}:[0-9a-f]{4} //')"
|
||||||
|
fi
|
||||||
|
if [ -z "$name" ] && [ -n "$idv" ]; then
|
||||||
|
vname="$(usb_vendor_name "$idv")"
|
||||||
|
name="${vname:-USB vendor $idv} device $idp"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*/sdio*)
|
||||||
|
# SDIO WiFi (common on Raspberry Pi and other ARM SBCs, e.g. Broadcom
|
||||||
|
# BCM43455) has no standard human-readable name in sysfs — the driver
|
||||||
|
# (brcmfmac, etc.) is usually the most reliable identifier here.
|
||||||
|
local vendor device
|
||||||
|
vendor="$(cat "$devpath/vendor" 2>/dev/null)"
|
||||||
|
device="$(cat "$devpath/device" 2>/dev/null)"
|
||||||
|
[ -n "$vendor" ] && name="SDIO vendor $vendor device $device"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "$name" ] && [ -n "$driver" ]; then
|
||||||
|
echo "$name (driver: $driver)"
|
||||||
|
elif [ -n "$name" ]; then
|
||||||
|
echo "$name"
|
||||||
|
elif [ -n "$driver" ]; then
|
||||||
|
echo "driver: $driver (chip model not identified)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- WiFi chip ---
|
||||||
wifi_chip="none"
|
wifi_chip="none"
|
||||||
wifi_iface=""
|
wifi_iface=""
|
||||||
for w in /sys/class/net/*/wireless; do
|
for w in /sys/class/net/*/wireless; do
|
||||||
@@ -100,24 +202,22 @@ for w in /sys/class/net/*/wireless; do
|
|||||||
break
|
break
|
||||||
done
|
done
|
||||||
if [ -n "$wifi_iface" ]; then
|
if [ -n "$wifi_iface" ]; then
|
||||||
if have lspci; then
|
wifi_chip="$(describe_net_device "$wifi_iface")"
|
||||||
wifi_chip="$(lspci | grep -iE 'network controller|wireless' | head -1 | sed -E 's/^[0-9a-f:.]+ [^:]+: //')"
|
|
||||||
fi
|
|
||||||
if [ -z "$wifi_chip" ] && have lsusb; then
|
|
||||||
wifi_chip="$(lsusb | grep -iE 'wireless|wifi|802\.11' | head -1 | sed -E 's/^Bus [0-9]+ Device [0-9]+: ID [0-9a-f]{4}:[0-9a-f]{4} //')"
|
|
||||||
fi
|
|
||||||
if [ -z "$wifi_chip" ] && [ -r "/sys/class/net/$wifi_iface/device/uevent" ]; then
|
|
||||||
driver="$(awk -F= '/^DRIVER=/ {print $2}' "/sys/class/net/$wifi_iface/device/uevent")"
|
|
||||||
[ -n "$driver" ] && wifi_chip="driver: $driver (chip model not auto-detected — check \`lsusb\`/\`lspci\` by hand)"
|
|
||||||
fi
|
|
||||||
[ -z "$wifi_chip" ] && wifi_chip="TODO — wireless interface $wifi_iface found but chip not identified"
|
[ -z "$wifi_chip" ] && wifi_chip="TODO — wireless interface $wifi_iface found but chip not identified"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- Ethernet chip (best-effort) ---
|
# --- Ethernet chip: first non-wireless, non-loopback interface with a real
|
||||||
|
# device behind it (best-effort — on a box with a cellular modem and no
|
||||||
|
# wired NIC this could pick the modem; double-check if that's your setup) ---
|
||||||
ethernet=""
|
ethernet=""
|
||||||
if have lspci; then
|
for e in /sys/class/net/*; do
|
||||||
ethernet="$(lspci | grep -i 'ethernet controller' | head -1 | sed -E 's/^[0-9a-f:.]+ [^:]+: //')"
|
iface="$(basename "$e")"
|
||||||
fi
|
[ "$iface" = "lo" ] && continue
|
||||||
|
[ -d "$e/wireless" ] && continue
|
||||||
|
[ -e "$e/device" ] || continue
|
||||||
|
ethernet="$(describe_net_device "$iface")"
|
||||||
|
[ -n "$ethernet" ] && break
|
||||||
|
done
|
||||||
|
|
||||||
today="$(date +%F 2>/dev/null || echo "TODO")"
|
today="$(date +%F 2>/dev/null || echo "TODO")"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user